When using SortedList on a ListView, it can happen that ListView won't update anymore, if the Comparator is poorly implemented. SortedList throws an ArrayIndexOutOfBoundsException.
I've seen this with a more complex Comparator, which returned inconsistent results, e.g. o1 didn't always compare to o2 in the opposite way as the o2 compares to the o1.
Implemeting well-behaved complex comparators is not always easy, so things like this can happen and I wonder if you can protect against it.
(for some reason I didn't even seen any stacktrace in my real app, my ListView just stopped udating itself and I wondered what's up).
Stacktrace:
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: -5
at javafx.collections.transformation.SortedList.findPosition(SortedList.java:318)
at javafx.collections.transformation.SortedList.removeFromMapping(SortedList.java:359)
at javafx.collections.transformation.SortedList.addRemove(SortedList.java:389)
at javafx.collections.transformation.SortedList.sourceChanged(SortedList.java:105)
at javafx.collections.transformation.TransformationList.lambda$getListener$16(TransformationList.java:106)
at javafx.collections.transformation.TransformationList$$Lambda$77/10143061.onChanged(Unknown Source)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:183)
Sample Code (just click the button a few times). You can also always return 1 from the comparator to trigger it immediately:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Random;
public class TestApp3 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(final Stage stage) throws Exception {
ObservableList<Integer> list = FXCollections.observableArrayList();
list.addAll(0, 2, 9, 6, 4, 5, 3, 1, 7, 8);
ListView<Integer> listView = new ListView<>();
listView.setItems(list.sorted((o1, o2) ->
// This is the bad comparator.
// if o1 = 7 and o2 == 8 returns 1
// if o1 = 8 and o2 == 7 also returns 1
// This seems to confuse SortedList
o1 < 5 ? o1.compareTo(o2) : 1));
Button button = new Button("Click me");
button.setOnAction(actionEvent -> {
// Remove the first item from list.
list.remove(0);
// Add new random integer between 0 (inclusive) and 10 (exclusive).
list.add(new Random().nextInt(10));
System.out.println(list);
});
Scene scene = new Scene(new VBox(listView, button));
stage.setScene(scene);
stage.show();
}
}
I've seen this with a more complex Comparator, which returned inconsistent results, e.g. o1 didn't always compare to o2 in the opposite way as the o2 compares to the o1.
Implemeting well-behaved complex comparators is not always easy, so things like this can happen and I wonder if you can protect against it.
(for some reason I didn't even seen any stacktrace in my real app, my ListView just stopped udating itself and I wondered what's up).
Stacktrace:
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: -5
at javafx.collections.transformation.SortedList.findPosition(SortedList.java:318)
at javafx.collections.transformation.SortedList.removeFromMapping(SortedList.java:359)
at javafx.collections.transformation.SortedList.addRemove(SortedList.java:389)
at javafx.collections.transformation.SortedList.sourceChanged(SortedList.java:105)
at javafx.collections.transformation.TransformationList.lambda$getListener$16(TransformationList.java:106)
at javafx.collections.transformation.TransformationList$$Lambda$77/10143061.onChanged(Unknown Source)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:183)
Sample Code (just click the button a few times). You can also always return 1 from the comparator to trigger it immediately:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Random;
public class TestApp3 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(final Stage stage) throws Exception {
ObservableList<Integer> list = FXCollections.observableArrayList();
list.addAll(0, 2, 9, 6, 4, 5, 3, 1, 7, 8);
ListView<Integer> listView = new ListView<>();
listView.setItems(list.sorted((o1, o2) ->
// This is the bad comparator.
// if o1 = 7 and o2 == 8 returns 1
// if o1 = 8 and o2 == 7 also returns 1
// This seems to confuse SortedList
o1 < 5 ? o1.compareTo(o2) : 1));
Button button = new Button("Click me");
button.setOnAction(actionEvent -> {
// Remove the first item from list.
list.remove(0);
// Add new random integer between 0 (inclusive) and 10 (exclusive).
list.add(new Random().nextInt(10));
System.out.println(list);
});
Scene scene = new Scene(new VBox(listView, button));
stage.setScene(scene);
stage.show();
}
}
- duplicates
-
JDK-8135017 ArrayIndexOutOfBoundsException with SortedList
-
- Closed
-
-
JDK-8134655 SortedList wrapping a FilteredList causes AIOOBE
-
- Resolved
-