Poblar JTable con gran cantidad de filas

Me gustaría rellenar una JTable durante el tiempo de ejecución con muchas filas (digamos 10000). Pero todos mis bashs son muy pobres e ineficientes.

El punto de partida es el método addData que obtiene una Lista de Objetos que representa una fila. Traté de llenar la tabla a través de un SwingWorker, pero esto solo funciona para pequeños datos para mí.

Otro bash fue establecer los datos directamente sin utilizar ningún tipo de subproceso, pero esto también es muy lento, al menos la interfaz de usuario no está bloqueada, como es el caso con el SwingWorker.

Entonces, ¿cómo se hace esto es general? La tabla debe llenarse fila por fila o por partes pero no todas por uno y la barra de desplazamiento vertical debe ser desplazable mientras tanto.

Mi TableModel:

 public class MyTableModel extends AbstractTableModel { /** * */ private static final long serialVersionUID = 1L; String[] columnNames; public Map data = new LinkedHashMap(); 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); } } 

Implementación de SwingWorker:

 class TableSwingWorker extends SwingWorker { private final MyTableModel tableModel; List messages; public TableSwingWorker(MyTableModel tableModel, List dataList) { this.tableModel = tableModel; this.messages = new LinkedList(mm); } @Override protected MyTableModel doInBackground() throws Exception { for(MyDataObject s : messages) { publish(s); } return tableModel; } @Override protected void process(List chunks) { for(MyDataObject row : chunks){ Long l = Long.parseLong(row.getId()); tableModel.addRow(l, row); } } } 

Agregar objetos a JTable:

 public void addData(List 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)); //} } 

Entonces, se han identificado una cantidad de cosas a partir de los comentarios …

  • Debe disparar correctamente el método insertado en fila, indicando solo aquellas filas que se han agregado y dónde se han actualizado. Esto es muy importante, ya que la tabla se ha optimizado para la velocidad
  • Debe proporcionar un método de adición por lotes para su modelo de tabla, lo que le permite agregar más filas múltiples en uno o en pocos pasos como sea posible.
  • Debería hacer que el SwingWorker duerma o ceda periódicamente para permitirle tiempo para publicar los resultados.

Entonces, en este ejemplo, estoy agregando 1, 000, 000 filas. En mi prueba, tomó un poco menos de 1 segundo …

 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 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 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 { 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 chunks) { System.out.println("Adding " + chunks.size() + " rows"); tableModel.addRows(chunks); } } }