For each sort graph you need some kind of model, which should hold the current state of each sort. This would probably mean adding your list of int
s to a separate list per sort.
You would also need some kind of mechanism that would allow you to loop through each sort algorithm and tell it to move to the next step in its algorithm, thus allowing you to control when each sort algorithm update and therefore control when the screen is updated.
Updated
Based on a comment from the OP, basically, I’ve ripped out the sort algorithm as a separate interface. Each algorithm would need to extend from this interface, but it provides the basic requirements to allow the UI to render the sort animation.
Bellow is basic implementation, while it’s based on Swing, if required, it wouldn’t be a stretch to get it to work with AWT.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestSort {
public static void main(String[] args) {
new TestSort();
}
public TestSort() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
SortPane sortPane = new SortPane();
int values[] = new int[10];
for (int index = 0; index < values.length; index++) {
values[index] = (int)Math.round(Math.random() * 100f);
}
BubbleSort sorter = new BubbleSort(values);
sortPane.setSorter(sorter);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(sortPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
sorter.sort();
}
});
}
public class SortPane extends JPanel {
private Sorter sorter;
private ChangeHandler changeHandler;
private int maxValue;
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int values[] = getSorter().getValues();
int width = getWidth() - 1;
int height = getHeight() - 1;
int colWidth = Math.round((float)width / (float)values.length);
int x = 0;
Color fill = Color.YELLOW;
Color highlight = null;
switch (getSorter().getState()) {
case Sorting:
fill = Color.BLUE;
highlight = Color.RED;
break;
case Done:
fill = Color.GREEN;
break;
}
for (int index = 0; index < values.length; index++) {
g2d.setColor(fill);
int value = values[index];
int colHeight = (int)((float)height * ((float)value / (float)maxValue));
g2d.fillRect(x, height - colHeight, colWidth - 1, colHeight);
if (getSorter().isActiveIndex(index) && highlight != null) {
g2d.setColor(highlight);
g2d.drawRect(x, height - colHeight, colWidth - 1, colHeight);
}
x += colWidth;
}
g2d.dispose();
}
public Sorter getSorter() {
return sorter;
}
public void setSorter(Sorter value) {
if (sorter != value) {
if (sorter != null) {
sorter.removeChangeListener(getChangeHandler());
}
sorter = value;
if (sorter != null) {
sorter.addChangeListener(getChangeHandler());
maxValue = 0;
for (int intValue : sorter.getValues()) {
maxValue = Math.max(maxValue, intValue);
}
}
repaint();
}
}
public ChangeHandler getChangeHandler() {
if (changeHandler == null) {
changeHandler = new ChangeHandler();
}
return changeHandler;
}
public class ChangeHandler implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
repaint();
}
}
}
public interface Sorter {
public enum State {
Waiting,
Sorting,
Done
}
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
public int[] getValues();
public void sort();
public State getState();
public boolean isActiveIndex(int index);
}
public abstract class AbstracSorter implements Sorter {
private List<ChangeListener> listeners;
private int[] values;
private State state = State.Waiting;
private List<Integer> activeIndices;
public AbstracSorter(int[] values) {
this.values = values;
listeners = new ArrayList<>(25);
activeIndices = new ArrayList<>(2);
}
@Override
public State getState() {
return state;
}
public void setState(State value) {
if (value != state) {
state = value;
fireStateChanged();
}
}
@Override
public int[] getValues() {
return values;
}
@Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
@Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
protected void fireStateChanged() {
if (listeners.size() > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}
@Override
public boolean isActiveIndex(int index) {
return activeIndices.contains(index);
}
protected void setActiveIndicies(int lower, int upper) {
activeIndices.clear();
activeIndices.add(lower);
activeIndices.add(upper);
fireStateChanged();
}
protected void swap(int[] anArrayOfInt, int i, int j) {
setActiveIndicies(i, j);
int x = anArrayOfInt[i];
anArrayOfInt[i] = anArrayOfInt[j];
anArrayOfInt[j] = x;
fireStateChanged();
}
}
public class BubbleSort extends AbstracSorter {
private int outter = 0;
private int inner = 0;
public BubbleSort(int[] values) {
super(values);
}
@Override
public void sort() {
setState(State.Sorting);
outter = 0;
inner = 1;
Timer timer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] values = getValues();
inner++;
if (inner >= values.length - outter) {
outter++;
inner = 1;
}
if (outter < values.length) {
if (values[inner - 1] > values[inner]) {
swap(values, inner - 1, inner);
} else {
setActiveIndicies(inner - 1, inner);
}
} else {
((Timer)e.getSource()).stop();
setState(State.Done);
}
}
});
timer.setRepeats(true);
timer.start();
}
}
}
Example using the source array
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestSort {
public static void main(String[] args) {
new TestSort();
}
private List<Sorter> sorters;
public TestSort() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
sorters = new ArrayList<>(2);
int values[] = new int[10];
for (int index = 0; index < values.length; index++) {
values[index] = (int) Math.round(Math.random() * 100f);
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(0, 2));
frame.add(createBubbleSortPane(values));
frame.add(createBubbleSortPane(values));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
for (Sorter sorter : sorters) {
sorter.sort();
}
}
});
}
protected SortPane createBubbleSortPane(int[] values) {
SortPane sortPane = new SortPane();
BubbleSort sorter = new BubbleSort(values);
sortPane.setSorter(sorter);
sortPane.setBorder(new CompoundBorder(new LineBorder(Color.GRAY), new EmptyBorder(8, 8, 8, 8)));
sorters.add(sorter);
return sortPane;
}
public class SortPane extends JPanel {
private Sorter sorter;
private ChangeHandler changeHandler;
private int maxValue;
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int values[] = getSorter().getValues();
Insets insets = getInsets();
int width = getWidth() - 1 - (insets.left + insets.right);
int height = getHeight() - 1 - (insets.top + insets.bottom);
int colWidth = Math.round((float) width / (float) values.length);
int x = insets.left;
Color fill = Color.YELLOW;
Color highlight = null;
switch (getSorter().getState()) {
case Sorting:
fill = Color.BLUE;
highlight = Color.RED;
break;
case Done:
fill = Color.GREEN;
break;
}
for (int index = 0; index < values.length; index++) {
g2d.setColor(fill);
int value = values[index];
int colHeight = (int) ((float) height * ((float) value / (float) maxValue));
g2d.fillRect(x, insets.top + height - colHeight, colWidth - 1, colHeight);
if (getSorter().isActiveIndex(index) && highlight != null) {
g2d.setColor(highlight);
g2d.drawRect(x, insets.top + height - colHeight, colWidth - 1, colHeight);
}
x += colWidth;
}
g2d.dispose();
}
public Sorter getSorter() {
return sorter;
}
public void setSorter(Sorter value) {
if (sorter != value) {
if (sorter != null) {
sorter.removeChangeListener(getChangeHandler());
}
sorter = value;
if (sorter != null) {
sorter.addChangeListener(getChangeHandler());
maxValue = 0;
for (int intValue : sorter.getValues()) {
maxValue = Math.max(maxValue, intValue);
}
}
repaint();
}
}
public ChangeHandler getChangeHandler() {
if (changeHandler == null) {
changeHandler = new ChangeHandler();
}
return changeHandler;
}
public class ChangeHandler implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
repaint();
}
}
}
public interface Sorter {
public enum State {
Waiting,
Sorting,
Done
}
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
public int[] getValues();
public void sort();
public State getState();
public boolean isActiveIndex(int index);
}
public abstract class AbstracSorter implements Sorter {
private List<ChangeListener> listeners;
private int[] values;
private State state = State.Waiting;
private List<Integer> activeIndices;
public AbstracSorter(int[] values) {
this.values = new int[values.length];
System.arraycopy(values, 0, this.values, 0, values.length);
listeners = new ArrayList<>(25);
activeIndices = new ArrayList<>(2);
}
@Override
public State getState() {
return state;
}
public void setState(State value) {
if (value != state) {
state = value;
fireStateChanged();
}
}
@Override
public int[] getValues() {
return values;
}
@Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
@Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
protected void fireStateChanged() {
if (listeners.size() > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}
@Override
public boolean isActiveIndex(int index) {
return activeIndices.contains(index);
}
protected void setActiveIndicies(int lower, int upper) {
activeIndices.clear();
activeIndices.add(lower);
activeIndices.add(upper);
fireStateChanged();
}
protected void swap(int[] anArrayOfInt, int i, int j) {
setActiveIndicies(i, j);
int x = anArrayOfInt[i];
anArrayOfInt[i] = anArrayOfInt[j];
anArrayOfInt[j] = x;
fireStateChanged();
}
}
public class BubbleSort extends AbstracSorter {
private int outter = 0;
private int inner = 0;
public BubbleSort(int[] values) {
super(values);
}
@Override
public void sort() {
setState(State.Sorting);
outter = 0;
inner = 1;
Timer timer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] values = getValues();
inner++;
if (inner >= values.length - outter) {
outter++;
inner = 1;
}
if (outter < values.length) {
if (values[inner - 1] > values[inner]) {
swap(values, inner - 1, inner);
} else {
setActiveIndicies(inner - 1, inner);
}
} else {
((Timer) e.getSource()).stop();
setState(State.Done);
}
}
});
timer.setRepeats(true);
timer.start();
}
}
}
Example Insertion Sorter
This is an example of using a Thread
as the primary sort engine instead of a Swing Timer
public class InsertionSorter extends AbstracSorter {
public InsertionSorter(int[] values) {
super(values);
}
@Override
public void sort() {
setState(State.Sorting);
new Thread(new SortRunnable()).start();
}
@Override
protected void swap(int[] anArrayOfInt, int i, int j) {
setActiveIndicies(i, j);
int x = anArrayOfInt[i];
anArrayOfInt[i] = anArrayOfInt[j];
anArrayOfInt[j] = x;
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
fireStateChanged();
}
});
} catch (InterruptedException | InvocationTargetException exp) {
exp.printStackTrace();
}
}
public class SortRunnable implements Runnable {
@Override
public void run() {
int[] values = getValues();
for (int i = 0; i < values.length; ++i) {
for (int j = i - 1; j >= 0 && values[j] > values[j + 1]; --j) {
try {
Thread.sleep(250);
} catch (InterruptedException ex) {
}
swap(values, j, j + 1);
}
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setState(State.Done);
}
});
}
}
}