首页 > 解决方案 > java - 如何按某个值对堆栈进行排序并在java中的DefaultListModel中显示它?

问题描述

我有下一个任务:实现 Java Swing 应用程序,它允许将新的矩形(由 X、Y 坐标和 a 和 b 边描述)添加到堆栈。应用程序还应该允许使用 LIFO 模型从堆栈中删除矩形。所有的矩形都应该出现在 JList 中。像这样:

在此处输入图像描述

我以这种方式实现:

public class Stack extends JFrame {

private JPanel contentPane;
private Deque<String> stack = new ArrayDeque<String>();
private DefaultListModel<String> dlm = new DefaultListModel<String>();



JList lstRectangle = new JList();
lstRectangle.setModel(dlm);     
scrollPane.setViewportView(lstRectangle);


JButton btnAdd = new JButton("Add rectangle");
        btnAdd.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                DlgRectangle dlgRectangle = new DlgRectangle();
                dlgRectangle.setVisible(true);
                if(dlgRectangle.isOk) {
                    dlm.add(0, "Upper left point: (" + dlgRectangle.txtX.getText() + "," + dlgRectangle.txtY.getText() + ") " + "width: " + dlgRectangle.txtWidth.getText() + " " + "height: " + dlgRectangle.txtHeight.getText());
                
                String rectangle = new String();
                rectangle = "Upper left point: (" + dlgRectangle.txtX.getText() + "," + dlgRectangle.txtY.getText() + ") " + "width: " + dlgRectangle.txtWidth.getText() + " " + "height: " + dlgRectangle.txtHeight.getText();
                stack.addFirst(rectangle);
                System.out.println(stack);
            }           
        }
    });


JButton btnDelete = new JButton("Delete rectangle");
        btnDelete.addActionListener(new ActionListener() {          
            @Override
            public void actionPerformed(ActionEvent e) {
                if(dlm != null && !dlm.isEmpty()) {
                    dlm.remove(0);
                    stack.pop();
                    System.out.println(stack);
                } else {
                    System.out.println("Stack is empty");
                }
            }
        });

首先我想问的是解决这个任务的好习惯和好方法。它正在工作,但我认为它可以做得更好,然后将对象推送到堆栈和 DefaultListModel。一些如何同时做到这一点并直接在 DLM 中显示堆栈对象。

下一个主要问题(对于类似任务)是如何根据矩形的面积对堆栈进行排序?

我试图改进上面的代码,但我只是计算了面积,然后卡住了......

int sideA = Integer.parseInt(dlgRectangle.txtWidth.getText());
                    int sideB = Integer.parseInt(dlgRectangle.txtHeight.getText());
                    int surfaceArea = sideA*sideB;
                    
                    System.out.println(surfaceArea);
                    
                    dlm.add(0, "Upper left point: (" + dlgRectangle.txtX.getText() + "," + dlgRectangle.txtY.getText() + ") " + "width: " + dlgRectangle.txtWidth.getText() + " " + "height: " + dlgRectangle.txtHeight.getText() + " " + "Surface area: " + String.valueOf(surfaceArea));                  
                    
                    String rectangle = new String();
                    rectangle = "Upper left point: (" + dlgRectangle.txtX.getText() + "," + dlgRectangle.txtY.getText() + ") " + "width: " + dlgRectangle.txtWidth.getText() + " " + "height: " + dlgRectangle.txtHeight.getText()+ " " + "Surface area: " + String.valueOf(surfaceArea);
                    stack.addFirst(rectangle);
                    stack.toArray();
                    
                    System.out.println(stack);

标签: javaswingsortingstackjlist

解决方案


首先我想问一下解决这个任务的好习惯和好方法

一般来说,没有。您正在运行一个支持模型/视图/控制器概念的 API。

模型的重点是对数据进行建模。观点是以某种有意义的方式向用户呈现数据。

将 a 应用于模型是没有意义String的。相反,您应该在模型中管理“矩形”的表示并配置JList以某种有意义的方式呈现此值。

请参阅如何使用列表编写自定义单元格渲染器

例如

简单模型/视图

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Rectangle;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class Stack {

    public static void main(String[] args) {
        new Stack();
    }

    public Stack() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JList<Rectangle> list;
        private DefaultListModel<Rectangle> model;

        public TestPane() {
            setLayout(new BorderLayout());
            list = new JList();
            model = new DefaultListModel<Rectangle>();

            list.setCellRenderer(new RectangeListCell());
            list.setModel(model);

            model.addElement(new Rectangle(100, 100, 10, 10));

            add(new JScrollPane(list));
        }

    }

    public class RectangeListCell extends DefaultListCellRenderer {

        @Override
        public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            if (value instanceof Rectangle) {
                Rectangle rect = (Rectangle) value;
                value = "Rectangle " + rect.x + "x" + rect.y + " by " + rect.width + "x" + rect.height;
            }
            return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        }

    }
}

下一个主要问题(对于类似任务)是如何根据矩形的面积对堆栈进行排序?

这是一个更难的问题。我建议从创建自己的 POJO 表示开始,它可以存储矩形的属性并计算它自己的面积。

下一个问题归结为欲望。

您可以计算新元素的位置并手动插入模型。这有点笨拙。

您可以创建一个“排序列表模型”。这要求将数据实际排序到模型上,例如......

public class SortedListModel<T> extends AbstractListModel<T> {

    private SortedSet<T> model;

    public SortedListModel(Comparator<T> comparator) {
        model = new TreeSet<>(comparator);
    }

    public void add(T value) {
        if (!model.contains(value)) {
            model.add(value);
            int insertIndex = model.headSet(value).size();

            fireIntervalAdded(value, insertIndex, insertIndex);
        }
    }

    @Override
    public int getSize() {
        return model.size();
    }

    @Override
    public T getElementAt(int index) {
        Object value = model.toArray()[index];
        return (T)value;
    }
}

这有点有限,因为它依赖于Set(我还没有实现删除,但这并不难)

另一个选择可能是实用程序我们使用的一个更“旧”的概念,之前JTable是可排序的,制作一个“包装器”模型,该模型将维护一个映射的索引列表,以便使数据看起来是排序的,例如

当然,这可能暗示只使用一个 column JTable

最后,您可以维护List数据并在每次添加新对象时手动对其进行排序,然后将其应用于模型。这是一些沉重的东西。

澄清

这是一些沉重的东西

这是指每次您想要实施解决方案时需要“重新发明”的工作量。

一个简单的解决方案可能会使用数据的单独“源模型”,当添加新数据时,需要对其进行排序,然后需要将“源模型”应用于ListModel,这会增加另一个开销,因为JList它将重新验证并重新粉刷自己。

“更好”的解决方案是:

  1. 易于重复使用
  2. 自给自足

最初的解决方案是基于一段非常古老的代码,它并不完全优雅或高性能。

稍微搜索研究一下,我们可以使用CollectionsAPI​​来计算插入点,这让我们可以使用一个简单的ArrayList代替,例如:

public class SortedListModel<T> extends AbstractListModel<T> {

    private List<T> model;
    private Comparator<T> comparator;

    public SortedListModel(Comparator<T> comparator) {
        model = new ArrayList<>(32);
        this.comparator = comparator;
    }

    public Comparator<T> getComparator() {
        return comparator;
    }

    public void add(T value) {
        int index = Collections.binarySearch(model, value, getComparator());
        if (index < 0) {
            index = -index - 1;
        }
        model.add(index, value);
        fireIntervalAdded(value, index, index);
    }

    @Override
    public int getSize() {
        return model.size();
    }

    @Override
    public T getElementAt(int index) {
        return model.get(index);
    }
}

推荐阅读