Delete row from JTable with custom table model
I've a subclass of JTable that uses a custom table model (an implementation of AbstractTableModel) to manage data.
The problem occurs when I try to delete a row with the method deleteRow. The row in my table is replaced by a blank string but the row is not deleted.
Here is the code:
public class LiveSearchTableModel extends AbstractTableModel { private List<String> columnNames = new ArrayList<String>(); private Map<Point, Object> displayData = new HashMap<Point, Object>(); private Map<Point, Object> originalData = new HashMap<Point, Object>(); public LiveSearchTableModel(List<String> columnNames, Map<Point, Object> tableData) { this.columnNames = columnNames; this.displayData = tableData; this.originalData.putAll(tableData); } @Override public int getColumnCount() { return columnNames.size(); } @Override public int getRowCount() { return displayData.size() / columnNames.size(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { return displayData.get(new Point(rowIndex, columnIndex)); } public void deleteRow (int row) { for (int cont = 0; cont < columnNames.size();cont++) { displayData.remove(new Point (row,cont)); } this.fireTableRowsDeleted(row, row); } @Override public void setValueAt(Object value, int rowIndex, int columnIndex) { this.displayData.put(new Point(rowIndex, columnIndex), value); this.fireTableDataChanged(); } }
Answers
The sscce below illustrates one potential problem: The keys of a Map are not ordered, while the rows of a table are. In the approach shown, an array of keys must by updated with each change to the data. See also this related example. If required, you could extend Point to implement Comparable, as shown here for Value.
import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.Point; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.AbstractTableModel; /** * @see http://stackoverflow.com/q/12330167/230513 */ public class TableModelTest extends JPanel { public TableModelTest() { super(new BorderLayout()); final MyModel model = new MyModel(); JTable table = new JTable(model); this.add(new JScrollPane(table)); this.add(new JButton(new AbstractAction("Delete") { @Override public void actionPerformed(ActionEvent e) { model.remove(0); } }), BorderLayout.SOUTH); } public class MyModel extends AbstractTableModel { private List<String> names = new ArrayList<String>(); private Map<Point, Object> data = new HashMap<Point, Object>(); private Point[] keys; public MyModel() { this.names = Arrays.asList("Point", "Name"); data.put(new Point(1, 1), "One"); data.put(new Point(2, 2), "Two"); data.put(new Point(3, 3), "Three"); this.keys = data.keySet().toArray(new Point[data.size()]); } public void remove(int row) { data.remove(keys[row]); keys = data.keySet().toArray(new Point[data.size()]); this.fireTableRowsDeleted(row, row); } @Override public int getColumnCount() { return names.size(); } @Override public String getColumnName(int col) { return names.get(col); } @Override public int getRowCount() { return data.size(); } @Override public Object getValueAt(int row, int col) { if (col == 0) { return keys[row]; } else { return data.get(keys[row]); } } } private void display() { JFrame f = new JFrame("TableModelTest"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(this); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { new TableModelTest().display(); } }); } }