Search code examples
javaswingtablemodel

Making an addRows() method for a Custom JTable TableModel


My explanation below rambles, boiling down, is there a way I can add a Row without firing off an event, such that I can add multiple rows and fire an event to update all of them at once? Without having to add code to contain the table data in the custom model?

I have a custom TableModel which extends from DefaultTableModel so that I can use DefaultTableModel to keep track of data for me, whilst still having some custom methods of my own.

The issue is, I was thinking it might be faster for me to have an "addRows(String[][] val)" method, when I wish to add multiple rows. I could then fire a single event, probably fireTableDataChanged() to update the rows all at once. For example, my current method:

JTable table1 = new JTable(new dgvTableModel(new String[] {<values>},0, new String[] {<values>}));
table1.addRow(new String[] {<values here>});
table1.addRow(new String[] {<values here>});
table1.addRow(new String[] {<values here>});

I would then repeat the above as many times as necessary. The issue is, each of those will fire off a seperate event. It would be much faster (I think), if I could do this using my custom table model:

JTable table1 = new JTable(new dgvTableModel(new String[] {<values>},0, new String[] {<values>}));
table1.addRows(new String[][] {{<values1 here}, {values2 here}, . . .}});

and then in the table model:

public void addRows(String[][] values) {
   for (String[] vals : values)           
       super.addRow(vals);
   }
   fireTableDataChanged();
}

I can code this in easily. The issue is again, that "super.addRow(vals);" line will fire an event each time through. Is there a way, without adding code to have my model contain the table data itself, to prevent that event being fired each time I add a row? Such that it waits for the fireTableDataChanged() call in the addRows method?

For reference, the code for my custom table model:

import java.awt.Color;
import java.util.ArrayList;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;

public class dgvTableModel extends DefaultTableModel {
//private DataTable tableVals = new DataTable();
private ArrayList<Color> rowColors;

//private ArrayList<Object[]> data = new ArrayList<>();
//default constructor has no data to begin with.
private int[] editableColumnNames;

public dgvTableModel(String[] colNames, int rowCount)
{
    super(colNames, rowCount);
}

public dgvTableModel(String[] colNames, int rowCount, String[] editableColNames)
{
    super(colNames, rowCount);
    //this.tableVals.setColNames(colNames);
    if (editableColNames!=null && editableColNames.length >0)
    {
        editableColumnNames = new int[editableColNames.length];
        int count = 0;
        for (int i =0; i< editableColNames.length;i++)
        {
            for (String val : colNames)
            {
                if (val.equalsIgnoreCase(editableColNames[i]))
                {
                    editableColumnNames[count] = i;
                    count+=1;
                    break;
                }
            }
        }
    }
}

public dgvTableModel(String[] colNames, int rowCount, String[] editableColNames, boolean colorChanges)
{
    super(colNames, rowCount);
    Color defColor = UIManager.getDefaults().getColor("Table.background");
    if (editableColNames!=null && editableColNames.length >0)
    {
        editableColumnNames = new int[editableColNames.length];
        int count = 0;
        if (colorChanges)
        {
            rowColors = new ArrayList<>();
        }
        for (int i =0; i< colNames.length;i++)
        {
            if (colorChanges)
            {
                rowColors.add(defColor);
            }

            for (String val : editableColNames)
            {
                if (val.equalsIgnoreCase(colNames[i]))
                {
                    editableColumnNames[count] = i;
                    count+=1;
                    break;
                }
            }
        }
    }
    else if (colorChanges)
    {
        rowColors = new ArrayList<>();
        for (String val : colNames)
        {
            rowColors.add(defColor);
        }
    }
}

@Override
public boolean isCellEditable(int row, int column)
{
    if(editableColumnNames!=null && editableColumnNames.length >0)
    {
        for (int colID : editableColumnNames)
        {
            if (column==colID)
                return true;
        }
    }
    return false;
}

public void setRowColor(int row, Color c)
{
    rowColors.set(row, c);
    fireTableRowsUpdated(row,row);
}

public Color getRowColor(int row)
{
    return rowColors.get(row);
}

@Override
public Class getColumnClass(int column)
{
    return String.class;
}

@Override
public String getValueAt(int row, int column)
{
    return super.getValueAt(row, column).toString();
}
}

Surely firing one event to display every row, is faster than firing one event for each row?


Solution

  • 'AbstractTableModel.fireTableDataChanged()' is used to indicate to the model (and the JTable UI which is notified by the model) that all possible data in the table may have changed and needs to be checked. This can (with emphasis on can) be an expensive operation. If you know which rows have been added, just use the 'AbstractTableModel.fireTableRowsInserted(int firstRow, int lastRow)' method instead. This will ensure only the effect rows are seen as changed. Take a look at all the fire* methods in AbstractTableModel. You can really exercise fine grained control over which rows, cells, etc are seen as dirty.

    Then again what your doing might be premature optimalization. Unless you have fiftythousand records in your JTable this is probably not going to be noticable. But if you have a massive amount of records in your JTable you might be beter of lazy loading them anyway.