一尘不染

用大量行填充JTable

java

我想在运行时用许多行填充JTable(说10000)。但是我所有的尝试都非常糟糕且效率低下。

起点是addData获取代表一行的对象列表的方法。我试图通过SwingWorker填写表格,但这对我来说仅适用于小数据。

另一个尝试是直接设置数据而不使用任何类型的线程,但这也非常慢,至少UI不会像SwingWorker那样被阻塞。

那么,您如何做到这一点呢?该表应逐行或逐块填充,但不能全部由一个填充,同时垂直滚动条应可滚动。

我的TableModel:

public class MyTableModel extends AbstractTableModel {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    String[] columnNames;   
    public Map<Long, ErrorMessage> data = new LinkedHashMap<Long, ErrorMessage>();

    public MyTableModel(String[] header) {
        columnNames = header;
    }

    public String getColumnName(int col) {
        return columnNames[col].toString();
    }

    @Override
    public int getColumnCount() {
        return columnNames.length;
    }

    @Override
    public int getRowCount() {
        return data.size();
    }


    @Override
    public Object getValueAt(int row, int col) {
            .
            .
        return value;
    }

    public void addRow(long id, MyDataObject o) {
            data.put(id, m);
            fireTableRowsInserted(0,nqm_messages.size()-1);         
    }

}

SwingWorker的实现:

class TableSwingWorker extends SwingWorker<MyTableModel, MyDataObject> {

    private final MyTableModel tableModel;
    List<MyDataObject> messages;

    public TableSwingWorker(MyTableModel tableModel, List<MyDataObject> dataList) {
        this.tableModel = tableModel;
            this.messages = new LinkedList<MyDataObject>(mm);
    }

    @Override
    protected MyTableModel doInBackground() throws Exception {

        for(MyDataObject s : messages) {
            publish(s);
        }

        return tableModel;
    }

    @Override
    protected void process(List<MyDataObject> chunks) {
        for(MyDataObject row : chunks){
            Long l = Long.parseLong(row.getId());
            tableModel.addRow(l, row);
        }
    }
}

将对象添加到JTable:

public void addData(List<MyDataObject> o) {

    MyTableModel m = (MyTableModel)table.getModel();

    (new TableSwingWorker(m,o)).execute();

    //for(int i=0; i < mm.size();i++) {
    //    long l = Long.parseLong(mm.get(i).getId());
    //    m.addRow(l, mm.get(i));
    //}
}

阅读 202

收藏
2020-09-08

共1个答案

一尘不染

因此,从评论中可以看出很多事情……

  • 您需要正确触发插入行的方法,仅指示已添加的行以及已更新的行。这非常重要,因为表格已经针对速度进行了优化
  • 您应该为表模型提供批处理添加方法,使您可以更轻松地通过一个步骤或更少的步骤添加多行
  • 您应该SwingWorker定期睡眠或屈服,以便有时间发布结果。

因此,在此示例中,我要添加1,000,000行。在我的测试中,花了不到1秒的时间…

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;

public class TestTableLoad01 {

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

    public TestTableLoad01() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                MyTableModel model = new MyTableModel();
                JTable table = new JTable(model);
                table.setDefaultRenderer(Date.class, new TimeCellRenderer());

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(table));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                TableSwingWorker worker = new TableSwingWorker(model);
                worker.execute();

            }
        });
    }

    public class TimeCellRenderer extends DefaultTableCellRenderer {

        private DateFormat df;

        public TimeCellRenderer() {
            df = new SimpleDateFormat("HH:mm:ss");
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

            if (value instanceof Date) {

                value = df.format(value);

            }

            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

            return this;

        }

    }

    public class MyTableModel extends AbstractTableModel {

        private String[] columnNames = new String[]{"Date", "Row"};
        private List<RowData> data;

        public MyTableModel() {
            data = new ArrayList<>(25);
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return columnIndex == 0 ? Date.class : Integer.class;
        }

        @Override
        public String getColumnName(int col) {
            return columnNames[col];
        }

        @Override
        public int getColumnCount() {
            return columnNames.length;
        }

        @Override
        public int getRowCount() {
            return data.size();
        }

        @Override
        public Object getValueAt(int row, int col) {
            RowData value = data.get(row);
            return col == 0 ? value.getDate() : value.getRow();
        }

        public void addRow(RowData value) {
            int rowCount = getRowCount();
            data.add(value);
            fireTableRowsInserted(rowCount, rowCount);
        }

        public void addRows(RowData... value) {
            addRows(Arrays.asList(value));
        }

        private void addRows(List<RowData> rows) {
            int rowCount = getRowCount();
            data.addAll(rows);
            fireTableRowsInserted(rowCount, getRowCount() - 1);
        }
    }

    public class RowData {

        private Date date;
        private int row;

        public RowData(int row) {
            this.date = new Date();
            this.row = row;
        }

        public Date getDate() {
            return date;
        }

        public int getRow() {
            return row;
        }
    }

    public class TableSwingWorker extends SwingWorker<MyTableModel, RowData> {

        private final MyTableModel tableModel;

        public TableSwingWorker(MyTableModel tableModel) {
            this.tableModel = tableModel;
        }

        @Override
        protected MyTableModel doInBackground() throws Exception {

            // This is a deliberate pause to allow the UI time to render
            Thread.sleep(2000);

            System.out.println("Start polulating");

            for (int index = 0; index < 1000000; index++) {

                RowData data = new RowData(index);
                publish(data);

                Thread.yield();

            }

            return tableModel;
        }

        @Override
        protected void process(List<RowData> chunks) {
            System.out.println("Adding " + chunks.size() + " rows");
            tableModel.addRows(chunks);
        }
    }
}
2020-09-08