1

I have made search everywhere so this is my 'last' hope.

I have a JTable which I populate with some CSV values. When I performe an action to remove selected rows, it does not remove all ... To understand better follow the debugs:

Begin (total selected): 6
Removed index: 6
Removed index: 4
Removed index: 3
Removed index: 2
Removed index: 1
End (total selected): 0

But is remaining one.... Note that 5 of them was removed, but one was skipped... I don't understand why this is happening.

My code:

    // I got a List<LikedHashMap<String, String>> data
    // where I store my CSV data.
    // To populate the JTable I remove from this list (data)
    // and insert to JTable.
    // Before remove from JTable, I put back the row into data.
    // THIS LOOP IS WORKING PRETTY WELL.
    for (int row : this.table.getSelectedRows()) {

        // Vamos recolocar esta linha na lista de não selecionados
        LinkedHashMap<String, String> newRow = new LinkedHashMap<>();

        // Vamos pegar todos os dados da linha
        for (int c = 0; c < this.headerCols.size(); c++) {
            newRow.put(
                this.headerCols.get(c),
                (String) this.tableModel.getValueAt(row, c)
            );
        }

        // Adiciona aos nao selecionados
        if (!this.data.contains(newRow)) {
            this.data.add(newRow);
        }
    }

    /**
     * MY PROBLEM ACTUALLY BEGINS HERE...
     */

    System.out.println("Begin (total selected): "+String.valueOf(this.table.getSelectedRowCount()));

    // Remove da tabela.
    while(this.table.getSelectedRow() != -1) {

        System.out.println("Removed item: "+String.valueOf(this.table.getSelectedRowCount()));

        this.tableModel.removeRow(this.table.getSelectedRow());
    }

    System.out.println("End (total selected): "+String.valueOf(this.table.getSelectedRowCount()));

Exception

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Invalid index
at javax.swing.DefaultRowSorter.convertRowIndexToModel(DefaultRowSorter.java:514)
at javax.swing.JTable.convertRowIndexToModel(JTable.java:2642)
at javax.swing.JTable.getValueAt(JTable.java:2717)
at javax.swing.JTable.prepareRenderer(JTable.java:5706)
at javax.swing.plaf.synth.SynthTableUI.paintCell(SynthTableUI.java:683)
at javax.swing.plaf.synth.SynthTableUI.paintCells(SynthTableUI.java:580)
at javax.swing.plaf.synth.SynthTableUI.paint(SynthTableUI.java:364)
at javax.swing.plaf.synth.SynthTableUI.update(SynthTableUI.java:275)
at javax.swing.JComponent.paintComponent(JComponent.java:780)
at javax.swing.JComponent.paint(JComponent.java:1056)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5219)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:290)
at javax.swing.RepaintManager.paint(RepaintManager.java:1265)
at javax.swing.JComponent._paintImmediately(JComponent.java:5167)
at javax.swing.JComponent.paintImmediately(JComponent.java:4978)
at javax.swing.RepaintManager$4.run(RepaintManager.java:824)
at javax.swing.RepaintManager$4.run(RepaintManager.java:807)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:807)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:782)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:731)
at javax.swing.RepaintManager.access$1300(RepaintManager.java:64)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1720)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Thank you very much, guys!

Marco A. Braghim
  • 516
  • 1
  • 5
  • 19

3 Answers3

3

You are incorrectly using a view-index to index the model. The problems in your code:


  1. Your inner for loop should look like this:

int rowModelId = convertRowIndexToModel( row );
for (int c = 0; c < headerCols.size(); c++) {
    newRow.put(
        headerCols.get(c),
        (String) tableModel.getValueAt(rowModelId, c)
    );
}

  1. Deleting the rows should be done like this:

while(table.getSelectedRow() != -1) {
    int rowModelId = convertRowIndexToModel( table.getSelectedRow() );
    System.out.println("Removed item: "+String.valueOf(this.table.getSelectedRowCount()));
    tableModel.removeRow(rowModelId);
}

You can learn more about view-indexes vs model-indexes from the Documentation on JTable introduction at the top. Some relevant quotes:

The JTable uses integers exclusively to refer to both the rows and the columns of the model that it displays. The JTable simply takes a tabular range of cells and uses getValueAt(int, int) to retrieve the values from the model during painting. It is important to remember that the column and row indexes returned by various JTable methods are in terms of the JTable (the view) and are not necessarily the same indexes used by the model.

By default, columns may be rearranged in the JTable so that the view's columns appear in a different order to the columns in the model. This does not affect the implementation of the model at all: when the columns are reordered, the JTable maintains the new order of the columns internally and converts its column indices before querying the model.

[...]The following shows how to convert coordinates from JTable to that of the underlying model:

int[] selection = table.getSelectedRows();
for (int i = 0; i < selection.length; i++) {
  selection[i] = table.convertRowIndexToModel(selection[i]);
}
// selection is now in terms of the underlying TableModel

I gave an answer to a similar question a while back that explains this difference between view and model. That case dealt with improper indexing of columns rather than rows, but the issue is comparable.

Community
  • 1
  • 1
TT.
  • 15,774
  • 6
  • 47
  • 88
  • Thank you man, I got it and just made this correction, but this is not the issue at all... If I comment this block of the code nothing change about the remove rows... =/ – Marco A. Braghim Jan 25 '16 at 18:39
  • @MarcoA.Braghim Then you didn't understand the things I wrote after the code I gave you. You need to convert the view indexes you get from `JTable.getSelectedRow` to model indexes using `convertRowIndexToModel` if you delete from the model. – TT. Jan 25 '16 at 18:45
  • @MarcoA.Braghim I've updated my answer to highlight a second problem regarding view-indices vs model-indexes. I do hope you read through the extra information I gave you so you won't make mistakes like this again. PS: having `this.` before each method call is unnecessary and gets boring to read real fast. – TT. Jan 25 '16 at 19:00
  • Hey dude, I'm feeling really really bad about it, because you was awesome trying to help me and wasting your time with my issue. I'm really grateful with you, thank you very much! However... I made exactly what you show me and it didn't work yet... =/ I'll edit the issue with my stack trace.. I'm really thinking about to jump from a bridge today, just for shame.... kkkk – Marco A. Braghim Jan 25 '16 at 19:12
  • 1
    @MarcoA.Braghim I'm not really wasting my time, I'm happy to help and educate people. Do not jump of a bridge, it's never worth it! Sometimes it's better to step back, take some time to relax, and start looking at your problem with a fresh spirit. A general remark though, is that it's usually a bad idea to extend JTable if you're not making a custom JTable control, rather just using it to display data. – TT. Jan 25 '16 at 19:29
  • @MarcoA.Braghim Second, seeing the exception you updated into your question I think the problem is no longer in the code-fragment you posted. This may be due to other methods in your JTable derived class. Without seeing the whole code it is hard to pinpoint this problem. It is always best to provide an [MVCE](https://stackoverflow.com/help/mcve) that illustrates the problem. Make a new very small program with just a frame with a JTable in it, have some rows in it, add a mouse listener to delete selected rows on right-click. Use the info I gave you and see if the problem persists. – TT. Jan 25 '16 at 19:38
  • Yes dude, I learn a lot today! And I think about to let the user remove one row at a time, it should work because the first row is always removed correctly... You think that Threads can cause this kind of thing? – Marco A. Braghim Jan 25 '16 at 19:44
  • 1
    @MarcoA.Braghim Oh yes, using Threads to interact with UI components is a bad idea. Interaction with UI components needs to be synchronized with the UI thread (also called [Event Dispatch Thread](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html) in Java terms). Swing objects are **not** Thread-Safe so interacting with them from other Threads will cause big problems. Interaction with Swing components from other threads is done using [SwingUtilities.invokeLater](https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html#invokeLater%28java.lang.Runnable%29) – TT. Jan 25 '16 at 19:52
0

Javadoc is your friend!

All of JTables row based methods are in terms of the RowSorter, which is not necessarily the same as that of the underlying TableModel. For example, the selection is always in terms of JTable so that when using RowSorter you will need to convert using convertRowIndexToView or convertRowIndexToModel.

Riaz
  • 874
  • 6
  • 15
  • Thank you for your answer, this really makes sense! So I tried this code without success... int row; while((row = this.table.getSelectedRow()) != -1) { this.tableModel.removeRow(this.table.convertRowIndexToView(row)); } =/ – Marco A. Braghim Jan 25 '16 at 17:04
  • The `table` is the view, and the `tableModel` is the model. So here you want to use `convertRowIndexToModel(row)`. – Riaz Jan 25 '16 at 17:19
0

As per Java doc jTable.getSelectedRow() returns the first selected row index. For the first loop everything is fine. But once you remove the row the rest of the rows will get new index after deletion of one row. So the row index now will be different for the rest of the rows remaining after deletion of one row.

The unsequenced debug report is because you have one row that is unselected in between the selected rows you are experimenting with.

Community
  • 1
  • 1
Kamal Singh
  • 990
  • 8
  • 13
  • Well, you're right dude. It's for that I use: while(this.table.getSelectedRow() != -1) { // ... } Already not working... =/ Thank you for your answer, sorry if I already don't got it... – Marco A. Braghim Jan 25 '16 at 18:45