Populate Jtable with Large Number of Rows

Populate JTable with large number of rows

So, a number of things have being identified from the comments...

  • You need to correctly fire the row inserted method, indicating only those rows that have being added and where they have being updated. This very important, as the the table has being optimised for speed
  • You should provide batch add method for your table model, allowing you to more easily add multiple rows in a single or as few steps as possible
  • You should have the SwingWorker periodically sleep or yield, to allow it time to publish the results.

So, in this example, I'm adding 1, 000, 000 rows. In my test it took slightly under 1 second...

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

JTable add rows according to length of array

How can I dynamically add rows to my table according to the size of
the array?

You can use a DefaultTableModel as table model and call addRow(Object[] rowData) method to dinamically add rows and automatically increasing the row's count. This way you can forget about a fixed row's count. For instance you may have something like this:

Object[] header = new Object[]{"Id", "Rijksregisternummer", "Naam", "Voornaam"
,"Straat", "Nummer", "Bus", "Postnummer"
,"Gemeente", "Telefoonnummer"};

DefaultTableModel model = new DefaultTableModel(header, 0);

for (int index = 0; index < arrpBag.length; index++) {
Object[] row = new Object[]{ arrpBag[index].getId()
, arrpBag[index].getRijksregisternummer()
, arrpBag[index].getNaam()
, arrpBag[index].getVoornaam()
, arrpBag[index].getStraat()
, arrpBag[index].getNummer()
, arrpBag[index].getBus()
, arrpBag[index].getPostnummer()
, arrpBag[index].getGemeente()
, arrpBag[index].getTelefoonnummer() };

model.addRow(row);
}

taTabelPersonen.setModel(model);

How Can I populate a JTable from an excel file, as long as there is more than 1 matching element in my Array List? [Java]

I was able to get this working, however, this only creates one row for the first matching value,

That is because in your "looping code" you create a new JTable each time.

Instead you want to create the table once and add data to your TableModel inside the loop. So the structure of your code should be something like:

String[] columnNames = { "Id", "Date", ... };
DefaultTableModel model = new DefaultTableModel(columnNames, 0);

for (...)
{
...

if (matched row found)
{
...

Object[] row = { val_0, val_1, ...};
model.addRow( row );
}

JTable table = new JTable( model );
...

How to count the number of rows of a JTable?

You can use the getRowCount() method:

Returns the number of rows that can be shown in the JTable, given
unlimited space. If a RowSorter with a filter has been specified,
the number of rows returned may differ from that of the underlying
TableModel.

Here you have one example:

import javax.swing.JTable;
public class Main {
public static void main(String[] argv) throws Exception {
Object[][] cellData = { { "1-1", "1-2" }, { "2-1", "2-2" } };
String[] columnNames = { "col1", "col2" };

JTable table = new JTable(cellData, columnNames);

int rows = table.getRowCount();
int cols = table.getColumnCount();

System.out.println(rows);
System.out.println(cols);
}
}

Java: Updating JTable with lots of rows in the background

This is due to Swing architecture. You must understand that the same thread where your listener is being executed is responsible for far more things (for example, refresh your UI).

I would use in your case a SwingWorker. This is a kind of Swing-specific background thread with special features to safely send updates to your UI as it executes its long task. You would do there your long task in background and call publish() for every relevant result it retrieves. Every time publish() is called, your process() method would be called (sending an update to the main thread under the hood), there you could update your table model.

Here you can find more information on SwingWorker:

http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html

Adding rows to a JTable

You should create a custom TableModel. A JTable doesn't actually store the rows, it always delegates that to a TableModel. To help you implementing it, you should make use of AbstractTableModel. Don't forget to call fireTableRowsInserted() every time you add rows. For better performances if you add a lot of rows, try to batch the updates and add many rows at a time.

How to fill data in a JTable with database?

I would recommend taking the following approach:

  • Create a Row class to represent a row read from your ResultSet. This could be a simple wrapper around an Object[].
  • Create a List<Row> collection, and subclass AbstractTableModel to be backed by this collection.
  • Use a SwingWorker to populate your List<Row> by reading from the underlying ResultSet on a background thread (i.e. within the doInBackground() method). Call SwingWorker's publish method to publish Rows back to the Event Dispatch thread (e.g. every 100 rows).
  • When the SwingWorker's process method is called with the latest chunk of Rows read, add them to your List<Row> and fire appropriate TableEvents to cause the display to update.
  • Also, use the ResultSetMetaData to determine the Class of each column within the TableModel definition. This will cause them to be rendered correctly (which won't be the case if you simply use a 2D Object[][] array).

The advantage of this approach is that the UI will not lock up when processing large ResultSets, and that the display will update incrementally as results are processed.

EDIT

Added example code below:

/**
* Simple wrapper around Object[] representing a row from the ResultSet.
*/
private class Row {
private final Object[] values;

public Row(Object[] values) {
this.values = values;
}

public int getSize() {
return values.length;
}

public Object getValue(int i) {
return values[i];
}
}

// TableModel implementation that will be populated by SwingWorker.
public class ResultSetTableModel extends AbstractTableModel {
private final ResultSetMetaData rsmd;
private final List<Row> rows;

public ResultSetTableModel(ResultSetMetaData rsmd) {
this.rsmd = rsmd;
this.rows = new ArrayList<Row>();
}

public int getRowCount() {
return rows.size();
}

public int getColumnCount() {
return rsmd.getColumnCount();
}

public Object getValue(int row, int column) {
return rows.get(row).getValue(column);
}

public String getColumnName(int col) {
return rsmd.getColumnName(col - 1); // ResultSetMetaData columns indexed from 1, not 0.
}

public Class<?> getColumnClass(int col) {
// TODO: Convert SQL type (int) returned by ResultSetMetaData.getType(col) to Java Class.
}
}

// SwingWorker implementation
new SwingWorker<Void, Row>() {
public Void doInBackground() {
// TODO: Process ResultSet and create Rows. Call publish() for every N rows created.
}

protected void process(Row... chunks) {
// TODO: Add to ResultSetTableModel List and fire TableEvent.
}
}.execute();

How to populate JTable from ResultSet?

I think the simplest way to build a model from an instance of ResultSet, could be as follows.

public static void main(String[] args) throws Exception {
// The Connection is obtained

ResultSet rs = stmt.executeQuery("select * from product_info");

// It creates and displays the table
JTable table = new JTable(buildTableModel(rs));

// Closes the Connection

JOptionPane.showMessageDialog(null, new JScrollPane(table));

}

The method buildTableModel:

public static DefaultTableModel buildTableModel(ResultSet rs)
throws SQLException {

ResultSetMetaData metaData = rs.getMetaData();

// names of columns
Vector<String> columnNames = new Vector<String>();
int columnCount = metaData.getColumnCount();
for (int column = 1; column <= columnCount; column++) {
columnNames.add(metaData.getColumnName(column));
}

// data of the table
Vector<Vector<Object>> data = new Vector<Vector<Object>>();
while (rs.next()) {
Vector<Object> vector = new Vector<Object>();
for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
vector.add(rs.getObject(columnIndex));
}
data.add(vector);
}

return new DefaultTableModel(data, columnNames);

}

UPDATE

Do you like to use javax.swing.SwingWorker? Do you like to use the try-with-resources statement?

public class GUI extends JFrame {

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new GUI().setVisible(true);
}
});
}

private final JButton button;
private final JTable table;
private final DefaultTableModel tableModel = new DefaultTableModel();

public GUI() throws HeadlessException {

setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

table = new JTable(tableModel);
add(new JScrollPane(table), BorderLayout.CENTER);

button = new JButton("Load Data");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
loadData();
return null;
}
}.execute();
}
});
add(button, BorderLayout.PAGE_START);

setSize(640, 480);
}

private void loadData() {
LOG.info("START loadData method");

button.setEnabled(false);

try (Connection conn = DriverManager.getConnection(url, usr, pwd);
Statement stmt = conn.createStatement()) {

ResultSet rs = stmt.executeQuery("select * from customer");
ResultSetMetaData metaData = rs.getMetaData();

// Names of columns
Vector<String> columnNames = new Vector<String>();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
columnNames.add(metaData.getColumnName(i));
}

// Data of the table
Vector<Vector<Object>> data = new Vector<Vector<Object>>();
while (rs.next()) {
Vector<Object> vector = new Vector<Object>();
for (int i = 1; i <= columnCount; i++) {
vector.add(rs.getObject(i));
}
data.add(vector);
}

tableModel.setDataVector(data, columnNames);
} catch (Exception e) {
LOG.log(Level.SEVERE, "Exception in Load Data", e);
}
button.setEnabled(true);

LOG.info("END loadData method");
}

}


Related Topics



Leave a reply



Submit