Between Java 7_71 and Java 8_31, the behaviour of getAddedSubList() has changed such that if there are 2 changes, the second call to change.next() returns items that are a part of the first change. This seems to happen in recursive scenarios where onChanged() has not yet returned, and a new change is triggered from with onChanged(). Please see attached test program, executing the main method. Run in version 7_71 to see the expected behaviour and in 8_31 to see the problematic behaviour. This also occurs in 8_25
The program has an observable list. Two items are added to the observable list at once using addAll(). Then from within the onChanged() method, another item is added using the add(). An if statement has been added to limit the recursive nature of this program to one recursive call. Upon adding the final element, onChanged() is called a second time, next() is called to obtain the changed elements using getAddedSubList() however, the sublist contains elements from the first change. The program writes to standard output, the item, along with the index of the change obtained from getFrom() and getTo()
In Java 7_71, the program returns as expected:
Change Detected: First One 0 2
Change Detected: Second One 0 2
Change Detected: One more 2 3
Here we see "First One" and "Second One" added as one batch, with the change ranging from index 0 to index 2. "One More", the final one, is added with the change going from index 2 to index 3
However, in Java 8_31:
Change Detected: First One 0 2
Change Detected: Second One 0 2
Change Detected: First One 0 3
Change Detected: Second One 0 3
Change Detected: One more 0 3
Here we see "First One" and "Second One" as expected, however in the recursive case, the change reports "First One". "Second One" again, along with "One More", with the changed indexed from 0 to 3.
This test program was distilled down from a more complex program which uses the ListChangeListener as a queue for dealing changes atomically, each item within the change has the potential to trigger other changes in recursive manner. By calling next(), the "change" should be fully represented and should not rely on the method returning in order for the state of the following change to be accurate.
--------test program------------
package com.test;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.stage.Stage;
public class ListChangeListenerBug extends Application {
private ObservableList<String> list = FXCollections.observableArrayList();
private boolean limitRecursion = true;//limit the recursion to one
@Override
public void start(Stage stage) {
list.addListener(new ListChangeListener<String>() {
@Override
public void onChanged(javafx.collections.ListChangeListener.Change<? extends String> c) {
c.next();
List<String> copiedList = new ArrayList<String>();
for (String string : c.getAddedSubList()) {//grab the items in the change and store them in a new collection as to not cause a concurrent list modification exception.
System.out.println("Change Detected: " + string + " " + c.getFrom() + " " + c.getTo());//write the change to console.
copiedList.add(string);
}
for (String string : copiedList) {
if(limitRecursion) {//make sure we only do the recursion step once.
limitRecursion = false;
list.add("One more");//cause another change for the list change handler to handle
}
}
}
});
List<String> original = new ArrayList<String>();
original.add("First One");
original.add("Second One");
list.addAll(original);//add two items at once
Platform.exit();
}
public static void main(String[] args) {
launch(args);
}
}
The program has an observable list. Two items are added to the observable list at once using addAll(). Then from within the onChanged() method, another item is added using the add(). An if statement has been added to limit the recursive nature of this program to one recursive call. Upon adding the final element, onChanged() is called a second time, next() is called to obtain the changed elements using getAddedSubList() however, the sublist contains elements from the first change. The program writes to standard output, the item, along with the index of the change obtained from getFrom() and getTo()
In Java 7_71, the program returns as expected:
Change Detected: First One 0 2
Change Detected: Second One 0 2
Change Detected: One more 2 3
Here we see "First One" and "Second One" added as one batch, with the change ranging from index 0 to index 2. "One More", the final one, is added with the change going from index 2 to index 3
However, in Java 8_31:
Change Detected: First One 0 2
Change Detected: Second One 0 2
Change Detected: First One 0 3
Change Detected: Second One 0 3
Change Detected: One more 0 3
Here we see "First One" and "Second One" as expected, however in the recursive case, the change reports "First One". "Second One" again, along with "One More", with the changed indexed from 0 to 3.
This test program was distilled down from a more complex program which uses the ListChangeListener as a queue for dealing changes atomically, each item within the change has the potential to trigger other changes in recursive manner. By calling next(), the "change" should be fully represented and should not rely on the method returning in order for the state of the following change to be accurate.
--------test program------------
package com.test;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.stage.Stage;
public class ListChangeListenerBug extends Application {
private ObservableList<String> list = FXCollections.observableArrayList();
private boolean limitRecursion = true;//limit the recursion to one
@Override
public void start(Stage stage) {
list.addListener(new ListChangeListener<String>() {
@Override
public void onChanged(javafx.collections.ListChangeListener.Change<? extends String> c) {
c.next();
List<String> copiedList = new ArrayList<String>();
for (String string : c.getAddedSubList()) {//grab the items in the change and store them in a new collection as to not cause a concurrent list modification exception.
System.out.println("Change Detected: " + string + " " + c.getFrom() + " " + c.getTo());//write the change to console.
copiedList.add(string);
}
for (String string : copiedList) {
if(limitRecursion) {//make sure we only do the recursion step once.
limitRecursion = false;
list.add("One more");//cause another change for the list change handler to handle
}
}
}
});
List<String> original = new ArrayList<String>();
original.add("First One");
original.add("Second One");
list.addAll(original);//add two items at once
Platform.exit();
}
public static void main(String[] args) {
launch(args);
}
}
- duplicates
-
JDK-8088022 [ListChangeBuilder] Simultaneous removal of multiple child nodes leads to exception
-
- Open
-