Swing lists and tables use an implementation of ListSelectionModel to handle keeping track of the selection. Every time you change the selection or rows then the component sends those changes to the selection model to keep it in sync. The default implementation that you get is DefaultListSelectionModel. Its implmentation is highly optimized for certain kinds of operations but has had to make comprimizes for a some other operations.
Back when I was doing Imagery I created an alternative implementation of ListSelectionModel because I needed an easy way to convert the selection into a SQL where clause. After much thought I came up with the idea of representing the selection as a list of ranges of selected ids. This means that you can then convert the selection into a list of "id >= rangeMin AND id <= rangeMax"
SQL expressions. This solution is well suited to the case where the selection is created by the user. The only way for a user to create more than one range is to "Control" click the rows. It is unlikely that the user will ever select more than a couple dozen ranges. This results in a model that is always simple however many rows there are in the List/Table. As a by product of this alternative selection model can be up to 88,000x faster than the default with large amounts of rows. I have put together a couple of JUnit tests, one does a huge amount of random operations on both a DefaultListSelectionModel and a RangeListSelectionModel and compares the results at each stage. This means that I can be very sure that my implementation is fully compatible with the default one. The second unit test does some performance tests to compare the two models. The data set is 1,000,000 rows and 20,000 operations for each test
Performance Results
Test | Default Model | Ranges Model | Perfomance Gain |
---|---|---|---|
Add selection interval | 0.017 s | 2.533 s | -149x |
Is selected index | 0.009 s | 0.011 s | -1.2x |
Remove index interval | 16:09 min | 0.011 s | 88,053x |
Set selected item | 1:30 min | 0.067 s | 1405x |
20k Random Operations | 4:25 min | 0.055 s | 4810x |
Average | 18,823x |
As you can see from the results that it is a large win on average. The test that it is slow at is AddSelectionInterval this is because it ends up with 17482 ranges. This should never happen in most real world applications, the only way I can see it happen is if you select all table rows that meet some search criteria which results in 1000s of random rows being selected. In all cases where the selection is user controlled then this will never happen.
Conclusion
I have seen bug reports coming in of hugely bad JTable performance when adding/removing rows from a huge table. After profiling I found that it was the SelectionModel causing the problems. You can see from the performance results that it can take minutes to do a large chunk of selection changes on a big table. So please try out my Ranges implementation in your application and tell me how it performs. If the responses are good I could change it to the default ListSelectionModel implementation in Java 7. To use it you just need to add the line "myTable.setSelectionModel(new RangeListSelectionModel());
".
Code Downloads
Here are the sources so you can try it out for yourself:
![]() RangeListSelectionModel.java | ![]() RangeSelectionModelPerformanceTest.java | ![]() RangeSelectionModelTest.java |
HI Jasper ,
Vary Nice 🙂 ,But I found bug in RangeListSelectionModel class
I use JXTable and if I’m loading data to table and selecting first row (Only this happen if one of table column set to sorted before loading data )
(sorry about my English)
java.lang.ArrayIndexOutOfBoundsException: -1
at org.jdesktop.swingx.decorator.ShuttleSorter.mapTowardModel(ShuttleSorter.java:96)
at org.jdesktop.swingx.decorator.Filter.convertRowIndexToModel(Filter.java:115)
at org.jdesktop.swingx.decorator.FilterPipeline.convertRowIndexToModel(FilterPipeline.java:419)
at org.jdesktop.swingx.decorator.SelectionMapper.convertToModel(SelectionMapper.java:191)
at org.jdesktop.swingx.decorator.SelectionMapper.updateFromViewSelectionChanged(SelectionMapper.java:201)
at org.jdesktop.swingx.decorator.SelectionMapper$2.valueChanged(SelectionMapper.java:258)
in line 554
protected void fireValueChanged(int firstIndex, int lastIndex, boolean isAdjusting) {
ListSelectionEvent e = null;
for (ListSelectionListener listener : listSelectionListeners) {
if (e == null) {
e = new ListSelectionEvent(this, firstIndex, lastIndex, isAdjusting);
}
listener.valueChanged(e);
}
}
Outch, it is outstanding. Great job. Is there any chance to put your ideas into SwingLabs so that RangeListSelectionModel could be the default SelectionModel for JXTable ?
Thanks.
I had similar problems with the default ListSelectionModel. I found that it uses a BitSet implementation to keep track of selections, but BitSet does not have shift operations which are badly needed when items get removed or inserted into the underlying ListModel of the ListSelectionModel. The ListSelectionModel instead does a bit by bit shift of the data using many method calls.
I managed to improve this by creating a better BitSet class that provides shift functions. Although the improvements are nowhere near as good as what you achieved, it still was a factor 15 faster using the custom shift functions (which shifts a long at a time instead of a bit at a time). It might be worthwhile extending BitSet with these functions as they’re a lot faster than the alternative. It is a shame the internal representation is private or it would have been possible to do this as a subclass.
Anyway, please make the new model the default. It took me quite a while to track down what was causing the incredible slowdown when removing items that were selected (with a 50000 entry list, with 25000 entries selected, and removing 25000 items, it takes roughly 10 seconds to complete which is pretty insane :-)).
If it was a same or better performance for all conditions then I would have done. Problem is its really helps some use cases but slows others. So at least you can grab it here and try as a alternative if you are seeing performance issues. There might be a way to detect if the current use fits one implementation or another and auto switch, but I have not looked into it. Jasper