1

我想在运行时用很多行(比如说 10000)填充一个 JTable。但是我所有的尝试都非常糟糕和低效。

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

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

那么你如何做到这一点是普遍的?表格应逐行或逐块填充,但不能全部填充,同时垂直滚动条应可滚动。

我的表模型:

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));
    //}
}
4

1 回答 1

11

所以,从评论中已经确定了一些事情......

  • 您需要正确触发行插入方法,仅指示那些已添加的行以及它们已更新的位置。这非常重要,因为表格已针对速度进行了优化
  • 您应该为您的表模型提供批量添加方法,让您更轻松地在单个或尽可能少的步骤中添加多行
  • 您应该有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);
        }
    }
}
于 2013-07-02T00:27:40.043 回答