Java 摇摆排序可视化
Java swing sort visualization
我是 java 编程的新手,我正在尝试进行排序可视化。
您如何看到 window 顶部(在 link 中)有一个 JTextArea,在这里我可以放置我的数组并按回车键程序将构建一个按钮矩阵。该程序将为每列 n 个按钮绘制。所以我可以用图形表示数字。当按下冒泡排序时,我想让看到冒泡排序的动画以图形方式对数组进行排序,但是当我这样做时,动画 class 中的 setBackground 没有做任何事情。
我使用了一个计时器,为什么我在互联网上发现 swing 不允许在短时间内多次调用重绘。
下面我放了一个 link 的 GUI。
代码:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
class animation implements ActionListener{
private JButton grid[][];
private int i;
private int j;
private int N;
animation(JButton grid[][], int N, int i, int j){
this.grid=grid;
this.N=N;
this.i=i;
this.j=j;
}
public void actionPerformed(ActionEvent e){
JButton tmp[]=new JButton[N];
for(int y=0;y<N;++y){
tmp[y]=new JButton();
tmp[y].setBackground(grid[y][i].getBackground());
}
for(int y=0;y<N;++y){
grid[y][i].setBackground(grid[y][j].getBackground());
grid[y][j].setBackground(tmp[y].getBackground());
}
return;
}
}
class sortInterface extends JFrame implements ActionListener{
//components declaration
private ArrayList<Integer> arr=new ArrayList<Integer>();
private JPanel simulation=new JPanel();
private JButton grid[][];
private JPanel algorithm=new JPanel();
private JPanel frontPanel=new JPanel();
private JLabel title=new JLabel("SORTING ALGORITHMS", JLabel.CENTER);
private JTextField input=new JTextField();
private JButton enter=new JButton("ENTER");
private JButton bubble=new JButton("Bubble Sort");
private JButton insertion=new JButton("Insertion Sort");
private JButton selection=new JButton("Selection Sort");
private JButton quick=new JButton("Quick Sort");
private JButton merge=new JButton("Merge Sort");
//here start the GUI
private void setFrame(){
setLayout(new BorderLayout());
add(frontPanel, "North");
add(simulation, "Center");
add(algorithm, "South");
setSize(720, 480);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
return;
}
private void setFrontPanel(){
frontPanel.setLayout(new BorderLayout());
frontPanel.add(title, "North");
frontPanel.add(input, "Center");
frontPanel.add(enter, "South");
return;
}
private void setAlgorithm(){
algorithm.add(bubble);
algorithm.add(insertion);
algorithm.add(selection);
algorithm.add(quick);
algorithm.add(merge);
return;
}
private void setListeners(){
enter.addActionListener(new buttonHandler(simulation, arr, input));
bubble.addActionListener(new buttonHandler(simulation, arr, input));
return;
}
//here start the computational part
private boolean isNumber(char car){
if(car=='1'||car=='2'||car=='3'||car=='4'||car=='5'||car=='6'||car=='7'||car=='8'||car=='9'||car=='0')
return true;
return false;
}
private int convertSubStringToInt(String read, int i){
String tmp="";
while(isNumber(read.charAt(i))&&i<read.length()){
tmp=tmp+String.valueOf(read.charAt(i));
++i;
}
return (Integer.parseInt(tmp));
}
private void addElementToArray(String read){
boolean flag=true;
for(int i=0;i<read.length();++i){
if(read.charAt(i)==' ')
flag=true;
if(isNumber(read.charAt(i))&&flag){
arr.add(convertSubStringToInt(read, i));
flag=false;
}
}
return;
}
private int findMax(){
int maxIndex=0;
for(int i=1;i<arr.size();++i)
if(arr.get(maxIndex)<arr.get(i))
maxIndex=i;
return arr.get(maxIndex);
}
private void setSimulation(){
int max=findMax();
int constMax=max;
grid=new JButton[max][arr.size()];
simulation.removeAll();
simulation.revalidate();
simulation.repaint();
simulation.setLayout(new GridLayout(max, arr.size()));
for(int i=0;i<constMax;++i){
for(int j=0;j<arr.size();++j){
grid[i][j]=new JButton();
if(arr.get(j)>=max)
grid[i][j].setBackground(Color.white);
else
grid[i][j].setBackground(Color.black);
simulation.add(grid[i][j]);
}
--max;
}
simulation.validate();
return;
}
sortInterface(){
setFrame();
setFrontPanel();
setAlgorithm();
setListeners();
}
public void actionPerformed(ActionEvent e){
String read=e.getActionCommand();
if(read=="ENTER"){
arr.clear();
addElementToArray(input.getText()+" ");
setSimulation();
}
else if(read=="Bubble Sort"){
for(int i=0;i<arr.size()-1;++i)
for(int j=0;j<arr.size()-1-i;++j)
if(arr.get(j)>arr.get(j+1)){
int tmp=arr.get(j);
arr.set(j, arr.get(j+1));
arr.set(j+1, tmp);
Timer time=new Timer(1, new animation(grid, arr.size(), j, j+1));
time.start();
try{
Thread.sleep(200);
}
catch(Exception sleep){
//prova
}
time.stop();
}
}
else if(read=="Insertion Sort"){
}
else if(read=="Selection Sort"){
}
else if(read=="Quick Sort"){
}
else{
}
return;
}
}
class projSortInterface
{
public static void main(String argv[])
{
sortInterface prova=new sortInterface();
prova.setVisible(true);
return;
}
}
GUI
您在事件处理程序中调用 Thread.sleep(200) - 不要。
Swing 是单线程的。它有一个线程(称为事件分派线程,EDT)可以完成所有事情:绘制、处理事件等。如果您让该线程休眠,它就无法执行任何其他操作,例如绘制或响应点击。
正如Joni所说,你不能睡在ActionListener中,因为每个ActionListener都是在Event Dispatch Thread中调用的。任何重大延迟都会阻止 Swing 绘制您的任何更改(并且还会阻止 Swing 响应任何用户输入)。
但是您可以执行您的逻辑并在不同的线程中休眠。您唯一需要记住的是,对 Swing 方法和 Swing 构造函数的调用可能不会在不同的线程中执行;当 运行在不同的线程中运行时,您必须使用 EventQueue.invokeLater 来执行这些方法调用。
使用新线程,它看起来像这样:
Runnable sortTask = () -> {
try {
for (int i=0; i < arr.size()-1; ++i) {
for (int j=0; j < arr.size()-1-i; ++j) {
if (arr.get(j) > arr.get(j+1)) {
int tmp = arr.get(j);
arr.set(j, arr.get(j+1));
arr.set(j+1, tmp);
int index1 = j;
int index2 = j+1;
EventQueue.invokeLater(() -> {
animation a =
new animation(grid, arr.size(), index1, index2);
a.actionPerformed(null);
});
Thread.sleep(200);
}
}
}
} catch (InterruptedException e) {
System.out.println("Interrupted. Terminating sort.");
}
};
new Thread(sortTask, "Sort").start();
如您所见,整个排序操作已移至 Runnable 中,然后传递给新的 Thread。实际的“动画”是在一个单独的小 Runnable 中完成的,它被传递给 EventQueue.invokeLater
,因为它包含 Swing 操作并且不允许 运行 在除事件调度线程之外的任何线程中。
可以使用计时器完成排序,但这需要重新安排您的逻辑。您将无法使用 for
循环。我认为在这种情况下创建线程并保留使用 for
将更具可读性和更易于使用。
一些其他注意事项:
- 请注意 try/catch 是 周围 整个排序。中断不是偶然发生的;如果有人打断了你的话题,这意味着他们要求你停止你正在做的事情并尽可能干净地退出。通过在程序将执行移动到
catch
块时允许 InterruptedException 退出循环,您将尊重此请求。
- 永远抓不到
Exception
。有许多未经检查的异常会暴露程序员的错误,例如 NullPointerException、IllegalArgumentException 和 IndexOutOfBoundsException。您 想要 那些终止您的程序,因为它们暴露了您作为程序员需要修复的错误。抑制它们不会使程序运行。
- 正如 George Z. 指出的那样,字符串是对象,您永远不应该使用
==
来比较对象,因为它不会检查它们的值是否相等,而是比较它们是否相同在程序中的相同位置创建的对象。 How do I compare strings in Java? 对此进行了详细解释。 (有一些特殊情况,可以安全地将对象与 ==
进行比较,但字符串绝对不在其中。)
- 需要
index1
和 index2
变量,因为只有最终变量可以传递给内部 类 和 lambda。它们不需要用 final
声明(尽管这是允许的);它们只需要 有效最终, 它们就是这样,因为它们只被赋值一次。
我是 java 编程的新手,我正在尝试进行排序可视化。
您如何看到 window 顶部(在 link 中)有一个 JTextArea,在这里我可以放置我的数组并按回车键程序将构建一个按钮矩阵。该程序将为每列 n 个按钮绘制。所以我可以用图形表示数字。当按下冒泡排序时,我想让看到冒泡排序的动画以图形方式对数组进行排序,但是当我这样做时,动画 class 中的 setBackground 没有做任何事情。
我使用了一个计时器,为什么我在互联网上发现 swing 不允许在短时间内多次调用重绘。
下面我放了一个 link 的 GUI。
代码:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
class animation implements ActionListener{
private JButton grid[][];
private int i;
private int j;
private int N;
animation(JButton grid[][], int N, int i, int j){
this.grid=grid;
this.N=N;
this.i=i;
this.j=j;
}
public void actionPerformed(ActionEvent e){
JButton tmp[]=new JButton[N];
for(int y=0;y<N;++y){
tmp[y]=new JButton();
tmp[y].setBackground(grid[y][i].getBackground());
}
for(int y=0;y<N;++y){
grid[y][i].setBackground(grid[y][j].getBackground());
grid[y][j].setBackground(tmp[y].getBackground());
}
return;
}
}
class sortInterface extends JFrame implements ActionListener{
//components declaration
private ArrayList<Integer> arr=new ArrayList<Integer>();
private JPanel simulation=new JPanel();
private JButton grid[][];
private JPanel algorithm=new JPanel();
private JPanel frontPanel=new JPanel();
private JLabel title=new JLabel("SORTING ALGORITHMS", JLabel.CENTER);
private JTextField input=new JTextField();
private JButton enter=new JButton("ENTER");
private JButton bubble=new JButton("Bubble Sort");
private JButton insertion=new JButton("Insertion Sort");
private JButton selection=new JButton("Selection Sort");
private JButton quick=new JButton("Quick Sort");
private JButton merge=new JButton("Merge Sort");
//here start the GUI
private void setFrame(){
setLayout(new BorderLayout());
add(frontPanel, "North");
add(simulation, "Center");
add(algorithm, "South");
setSize(720, 480);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
return;
}
private void setFrontPanel(){
frontPanel.setLayout(new BorderLayout());
frontPanel.add(title, "North");
frontPanel.add(input, "Center");
frontPanel.add(enter, "South");
return;
}
private void setAlgorithm(){
algorithm.add(bubble);
algorithm.add(insertion);
algorithm.add(selection);
algorithm.add(quick);
algorithm.add(merge);
return;
}
private void setListeners(){
enter.addActionListener(new buttonHandler(simulation, arr, input));
bubble.addActionListener(new buttonHandler(simulation, arr, input));
return;
}
//here start the computational part
private boolean isNumber(char car){
if(car=='1'||car=='2'||car=='3'||car=='4'||car=='5'||car=='6'||car=='7'||car=='8'||car=='9'||car=='0')
return true;
return false;
}
private int convertSubStringToInt(String read, int i){
String tmp="";
while(isNumber(read.charAt(i))&&i<read.length()){
tmp=tmp+String.valueOf(read.charAt(i));
++i;
}
return (Integer.parseInt(tmp));
}
private void addElementToArray(String read){
boolean flag=true;
for(int i=0;i<read.length();++i){
if(read.charAt(i)==' ')
flag=true;
if(isNumber(read.charAt(i))&&flag){
arr.add(convertSubStringToInt(read, i));
flag=false;
}
}
return;
}
private int findMax(){
int maxIndex=0;
for(int i=1;i<arr.size();++i)
if(arr.get(maxIndex)<arr.get(i))
maxIndex=i;
return arr.get(maxIndex);
}
private void setSimulation(){
int max=findMax();
int constMax=max;
grid=new JButton[max][arr.size()];
simulation.removeAll();
simulation.revalidate();
simulation.repaint();
simulation.setLayout(new GridLayout(max, arr.size()));
for(int i=0;i<constMax;++i){
for(int j=0;j<arr.size();++j){
grid[i][j]=new JButton();
if(arr.get(j)>=max)
grid[i][j].setBackground(Color.white);
else
grid[i][j].setBackground(Color.black);
simulation.add(grid[i][j]);
}
--max;
}
simulation.validate();
return;
}
sortInterface(){
setFrame();
setFrontPanel();
setAlgorithm();
setListeners();
}
public void actionPerformed(ActionEvent e){
String read=e.getActionCommand();
if(read=="ENTER"){
arr.clear();
addElementToArray(input.getText()+" ");
setSimulation();
}
else if(read=="Bubble Sort"){
for(int i=0;i<arr.size()-1;++i)
for(int j=0;j<arr.size()-1-i;++j)
if(arr.get(j)>arr.get(j+1)){
int tmp=arr.get(j);
arr.set(j, arr.get(j+1));
arr.set(j+1, tmp);
Timer time=new Timer(1, new animation(grid, arr.size(), j, j+1));
time.start();
try{
Thread.sleep(200);
}
catch(Exception sleep){
//prova
}
time.stop();
}
}
else if(read=="Insertion Sort"){
}
else if(read=="Selection Sort"){
}
else if(read=="Quick Sort"){
}
else{
}
return;
}
}
class projSortInterface
{
public static void main(String argv[])
{
sortInterface prova=new sortInterface();
prova.setVisible(true);
return;
}
}
GUI
您在事件处理程序中调用 Thread.sleep(200) - 不要。
Swing 是单线程的。它有一个线程(称为事件分派线程,EDT)可以完成所有事情:绘制、处理事件等。如果您让该线程休眠,它就无法执行任何其他操作,例如绘制或响应点击。
正如Joni所说,你不能睡在ActionListener中,因为每个ActionListener都是在Event Dispatch Thread中调用的。任何重大延迟都会阻止 Swing 绘制您的任何更改(并且还会阻止 Swing 响应任何用户输入)。
但是您可以执行您的逻辑并在不同的线程中休眠。您唯一需要记住的是,对 Swing 方法和 Swing 构造函数的调用可能不会在不同的线程中执行;当 运行在不同的线程中运行时,您必须使用 EventQueue.invokeLater 来执行这些方法调用。
使用新线程,它看起来像这样:
Runnable sortTask = () -> {
try {
for (int i=0; i < arr.size()-1; ++i) {
for (int j=0; j < arr.size()-1-i; ++j) {
if (arr.get(j) > arr.get(j+1)) {
int tmp = arr.get(j);
arr.set(j, arr.get(j+1));
arr.set(j+1, tmp);
int index1 = j;
int index2 = j+1;
EventQueue.invokeLater(() -> {
animation a =
new animation(grid, arr.size(), index1, index2);
a.actionPerformed(null);
});
Thread.sleep(200);
}
}
}
} catch (InterruptedException e) {
System.out.println("Interrupted. Terminating sort.");
}
};
new Thread(sortTask, "Sort").start();
如您所见,整个排序操作已移至 Runnable 中,然后传递给新的 Thread。实际的“动画”是在一个单独的小 Runnable 中完成的,它被传递给 EventQueue.invokeLater
,因为它包含 Swing 操作并且不允许 运行 在除事件调度线程之外的任何线程中。
可以使用计时器完成排序,但这需要重新安排您的逻辑。您将无法使用 for
循环。我认为在这种情况下创建线程并保留使用 for
将更具可读性和更易于使用。
一些其他注意事项:
- 请注意 try/catch 是 周围 整个排序。中断不是偶然发生的;如果有人打断了你的话题,这意味着他们要求你停止你正在做的事情并尽可能干净地退出。通过在程序将执行移动到
catch
块时允许 InterruptedException 退出循环,您将尊重此请求。 - 永远抓不到
Exception
。有许多未经检查的异常会暴露程序员的错误,例如 NullPointerException、IllegalArgumentException 和 IndexOutOfBoundsException。您 想要 那些终止您的程序,因为它们暴露了您作为程序员需要修复的错误。抑制它们不会使程序运行。 - 正如 George Z. 指出的那样,字符串是对象,您永远不应该使用
==
来比较对象,因为它不会检查它们的值是否相等,而是比较它们是否相同在程序中的相同位置创建的对象。 How do I compare strings in Java? 对此进行了详细解释。 (有一些特殊情况,可以安全地将对象与==
进行比较,但字符串绝对不在其中。) - 需要
index1
和index2
变量,因为只有最终变量可以传递给内部 类 和 lambda。它们不需要用final
声明(尽管这是允许的);它们只需要 有效最终, 它们就是这样,因为它们只被赋值一次。