FULL PRODUCT VERSION :
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Windows 10 Version 1511 Build 10586.545
A DESCRIPTION OF THE PROBLEM :
If you create a ListView instance and add a ListChangeListener to the selected items, it may happen that the list that is accessible via the Change object is modified while the onChanged method is executed. This happened to me for a Change which carried an addition to the list, as well as a removal. It seems to be happening consistently on the same machine. I could notice different behavior only if I switched computers.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I added source code to reproduce the problem. If you execute it, you need to do the following:
1. Select the second item in the list
2. Hold down shift and click on the first item
3. Still holding shift click on the second item again
As this wasn't consistently happening to me on different computers, you may need to select the third item instead of the first one in step 2.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
An event is fired which does not alter it's state while being processed.
ACTUAL -
The list backing the Change instance changes. At first, it has a null reference. After the thread slept for around 300ms (on my machine, this may vary) there is one String instance in the list. At home, this happens for me on the removal change, at my work place it happened in the added change.
For me, the log file looks like this:
--- Next change (added/removed/permuted/replaced/updated) false true false false false
--- (change.getList().size()/added/removed) 1 0 1
= before sleep
item #0: null
= after sleep
item #0: item 2
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Demo extends Application {
public static void main(String args[]) {
launch(Demo.class);
}
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Listchangelistener Problem");
ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
listView.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<String>() {
public void onChanged(Change<? extends String> change) {
while (change.next()) {
System.out.println(String.format("--- Next change (added/removed/permuted/replaced/updated) %b %b %b %b %b", change.wasAdded(), change.wasRemoved(), change.wasPermutated(), change.wasReplaced(), change.wasUpdated()));
System.out.println(String.format("--- (change.getList().size()/added/removed) %d %d %d", change.getList().size(), change.getAddedSize(), change.getRemovedSize()));
System.out.println("= before sleep");
printList(change);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("= after sleep");
printList(change);
}
}
private void printList(Change<? extends String> change) {
int count = 0;
for (String entry : change.getList()) {
System.out.println("item #" + count++ + ": " + entry);
}
}
});
listView.getItems().add("test");
listView.getItems().add("more test");
listView.getItems().add("another test");
primaryStage.setScene(new Scene(listView, 400, 600));
primaryStage.show();
}
}
---------- END SOURCE ----------