I am trying to code a sorting algorithm visualizer. I put almost everything into one class called visualizer and I was wondering how I can move my "insertionSort" and "bubbleSort" methods into separate classes without breaking my code?
I want to do this because when I integrate merge sort and quick sort I want to keep them in the same class as there helper methods.
public class Visualizer extends JPanel implements ActionListener{
//Initialize Variables
final int SCREEN_WIDTH = 1280;
final int SCREEN_HEIGHT = 720;
final int BAR_HEIGHT = SCREEN_HEIGHT * 4/5;
final int BAR_WIDTH = 5;
final int NUM_BARS = SCREEN_WIDTH/BAR_WIDTH;
JButton bubbleSort = new JButton();
JButton insertSort = new JButton();
SwingWorker<Void,Void> shuffler, sorter;
int[] array = new int[NUM_BARS];
int current;
int traverse;
public Visualizer() {
setBackground(Color.darkGray);
setPreferredSize(new Dimension());
//Initialize Bar Height
for (int i = 0; i < NUM_BARS; i++) {
array[i] = i * BAR_HEIGHT / NUM_BARS;
}
//InsertionSort Button
insertSort.setText("Insertion Sort");
this.add(insertSort);
insertSort.addActionListener(this);
//BubbleSort Button
bubbleSort.setText("Bubble Sort");
this.add(bubbleSort);
bubbleSort.addActionListener(this);
}
public void shuffleArray() {
shuffler = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws InterruptedException {
Random random = new Random();
for (int i = 0; i < NUM_BARS; i++) {
int swap = random.nextInt(NUM_BARS - 1);
swap(i, swap);
Thread.sleep(10);
repaint();
}
return null;
}
@Override
public void done() {
super.done();
sorter.execute();
}
};
shuffler.execute();
}
public void insertionSort() {
sorter = new SwingWorker<Void, Void>() {
@Override
public Void doInBackground() throws InterruptedException {
for (current = 1; current < NUM_BARS; current++) {
traverse = current;
while (traverse > 0 && array[traverse] < array[traverse - 1]) {
swap(traverse, traverse - 1);
traverse--;
Thread.sleep(1);
repaint();
}
}
current = 0;
traverse = 0;
return null;
}
};
}
public void bubbleSort() {
sorter = new SwingWorker<Void, Void>() {
@Override
public Void doInBackground() throws InterruptedException {
for(current = 0; current < NUM_BARS; current++) {
for(traverse = 1; traverse < (NUM_BARS - current); traverse++) {
if(array[traverse-1] > array[traverse]) {
swap(traverse, traverse-1);
traverse--;
Thread.sleep(1);
repaint();
}
}
}
current = 0;
traverse = 0;
return null;
}
};
}
public void quickSort() {
sorter = new SwingWorker<Void, Void>() {
@Override
public Void doInBackground() throws InterruptedException {
return null;
}
};
}
public void swap(int indexOne, int indexTwo) {
int temp = array[indexOne];
array[indexOne] = array[indexTwo];
array[indexTwo] = temp;
}
@Override
public void paintComponent(Graphics g) {
Graphics2D graphics = (Graphics2D)g;
super.paintComponent(graphics);
g.setColor(Color.white);
for(int i = 0; i < NUM_BARS; i++) {
graphics.fillRect(i * BAR_WIDTH,SCREEN_HEIGHT-array[i],BAR_WIDTH,array[i]);
}
g.setColor(Color.green);
graphics.fillRect(current*BAR_WIDTH,SCREEN_HEIGHT-array[current], BAR_WIDTH, array[current]);
g.setColor(Color.red);
graphics.fillRect(traverse*BAR_WIDTH,SCREEN_HEIGHT-array[traverse], BAR_WIDTH, array[traverse]);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == insertSort) {
insertionSort();
shuffleArray();
}
else if (e.getSource() == bubbleSort) {
bubbleSort();
shuffleArray();
}
}
}
This is not as easy as it might seem, but, the basic answer comes down to making use of models and observers (something like the "model-view-controller" concept), so you can decouple the workflows in a more meaningful way.
One important note to make is, Swing is NOT thread save. This means that you should not be modifying the UI or any state the UI relies on from out side the context the Event Dispatching Thread. Under your current workflow it's possible for the
SwingWorkerto modify the state of the array (and other state values) while the UI is been painted, this could cause no end of issues.The core functionality of a sorter is basically the same, it needs some values, needs to be able to swap those values and needs to deliver notifications to interested parties that some kind of state has changed.
Okay, pretty basic, but the nice idea behind this anything that changes the values can be used, for example, we can shuffle the values through the interface...
And the insertion sorter...
The idea here is the sorters will generate events telling what's changed, so you can apply those changes to the view independently of the sorters, so you don't risk dirty updates.
And finally, the
SwingWorkerNow, this will shuffle the values and the re-sort them as a single unit of work.
Runnable example...
Food for thought...
A different approach would be to sort the values and in the process generate a series of "events" which describe what actions are been carried out. This would allow you to take the same starting data and apply those actions in a more controlled manner (using a Swing
Timerfor example). This would reduce some of the overhead of having to copy the array data each time an update occurs.If you're interested, I've done an example here