Incrementing modCount after modifying the backing may cause a mismatch between a sub-list's modCount and the backing list modCount, leading to a ConcurrentModificationError.
For example, the call to list.add("ham") in the sample below will cause the listener to be invoked wherein the listener creates a sub-list of list. The listener code is executed before the add method exits. Since modCount is incremented after the element is added to the backing list and the listener code is excuted before modCount is incremented, the list and sub-list will have different modCount values even though there haven't been any other modifications to either list.
{code}
private static class Mod {
Mod(List<String> added) {
this.added = added;
}
@Override public String toString() {
return added.toString();
}
final private List<String> added;
}
private List<Mod> mods = new ArrayList<>();
public static void main(String[] args) {
ObservableList<String> list = new VetoableListDecorator<String>(FXCollections.<String>observableArrayList()) {
@Override
protected void onProposedChange(List<String> added, int[] removed) {
// do nothing
}
};
list.addAll("green ham");
list.addListener(new ListChangeListener<String>() {
@Override
public void onChanged(Change<? extends String> c) {
while(c.next()) {
if (c.wasAdded()) mods.add(new Mod((List<String>) c.getAddedSubList()));
}
}
});
list.add("ham");
System.out.println("mods: " + mods.toString());
{code}
For example, the call to list.add("ham") in the sample below will cause the listener to be invoked wherein the listener creates a sub-list of list. The listener code is executed before the add method exits. Since modCount is incremented after the element is added to the backing list and the listener code is excuted before modCount is incremented, the list and sub-list will have different modCount values even though there haven't been any other modifications to either list.
{code}
private static class Mod {
Mod(List<String> added) {
this.added = added;
}
@Override public String toString() {
return added.toString();
}
final private List<String> added;
}
private List<Mod> mods = new ArrayList<>();
public static void main(String[] args) {
ObservableList<String> list = new VetoableListDecorator<String>(FXCollections.<String>observableArrayList()) {
@Override
protected void onProposedChange(List<String> added, int[] removed) {
// do nothing
}
};
list.addAll("green ham");
list.addListener(new ListChangeListener<String>() {
@Override
public void onChanged(Change<? extends String> c) {
while(c.next()) {
if (c.wasAdded()) mods.add(new Mod((List<String>) c.getAddedSubList()));
}
}
});
list.add("ham");
System.out.println("mods: " + mods.toString());
{code}
- relates to
-
JDK-8091751 ObservableListWrapper could return false from setAll in certain cases
- Open