FULL PRODUCT VERSION :
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
SortedList causes uncaught ArrayIndexOutOfBoundsException when resorting.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the code provided
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expect it not to throw uncatchable exception
ACTUAL -
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -6
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$15(TransformationList.java:106)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
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.set(ModifiableObservableListBase.java:163)
at test.collections.TestListUpdate.lambda$3(TestListUpdate.java:119)
at com.sun.javafx.collections.MapListenerHelper$SingleChange.fireValueChangedEvent(MapListenerHelper.java:163)
at com.sun.javafx.collections.MapListenerHelper.fireValueChangedEvent(MapListenerHelper.java:72)
at com.sun.javafx.collections.ObservableMapWrapper.callObservers(ObservableMapWrapper.java:115)
at com.sun.javafx.collections.ObservableMapWrapper.put(ObservableMapWrapper.java:173)
at test.collections.TestListUpdate$Person.setAge(TestListUpdate.java:22)
at test.collections.TestListUpdate.main(TestListUpdate.java:136)
ERROR MESSAGES/STACK TRACES THAT OCCUR :
added 0x1e80bfe8 [bob, 0yo]
added 0x50040f0c [john, 0yo]
return -8; [bob, 0yo] < [john, 0yo]
added 0x2dda6444 [joe, 0yo]
return -8; [bob, 0yo] < [joe, 0yo]
return 3; [john, 0yo] > [joe, 0yo]
added 0x5e9f23b4 [ed, 0yo]
return 5; [joe, 0yo] > [ed, 0yo]
return -3; [bob, 0yo] < [ed, 0yo]
added 0x4783da3f [fred, 0yo]
return -1; [ed, 0yo] < [fred, 0yo]
return 4; [joe, 0yo] > [fred, 0yo]
--------------------------------------------------------
get person @ index 3
set age of 'ed' @ index 3 to 73
'ed' @ 3; 'age':[73] added
reset 'ed' @ 3
replaced 0x5e9f23b4 [ed, 73yo] with 0x5e9f23b4 [ed, 73yo]
return -73; [fred, 0yo] < [ed, 73yo]
return -73; [joe, 0yo] < [ed, 73yo]
return -73; [john, 0yo] < [ed, 73yo]
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -6
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$15(TransformationList.java:106)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
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.set(ModifiableObservableListBase.java:163)
at test.collections.TestListUpdate.lambda$3(TestListUpdate.java:119)
at com.sun.javafx.collections.MapListenerHelper$SingleChange.fireValueChangedEvent(MapListenerHelper.java:163)
at com.sun.javafx.collections.MapListenerHelper.fireValueChangedEvent(MapListenerHelper.java:72)
at com.sun.javafx.collections.ObservableMapWrapper.callObservers(ObservableMapWrapper.java:115)
at com.sun.javafx.collections.ObservableMapWrapper.put(ObservableMapWrapper.java:173)
at test.collections.TestListUpdate$Person.setAge(TestListUpdate.java:22)
at test.collections.TestListUpdate.main(TestListUpdate.java:136)
REPRODUCIBILITY :
This bug can be reproduced often.
---------- BEGIN SOURCE ----------
package test.collections;
import java.util.Arrays;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
public class TestListUpdate {
static class Person {
ObservableMap<String, Object> data = FXCollections.observableHashMap();
public Person(String name) {
setName(name);
}
void setName(String name) { data.put("name", name); }
String getName() { return (String) data.get("name"); }
void setAge(int age) { data.put("age", age); }
int getAge() { return (int) data.getOrDefault("age", 0); }
@Override public String toString() { return String.format("%s, %d", getName(), getAge()); }
}
public static void main(String[] args) {
// create observable list of people
ObservableList<Person> people = FXCollections.observableArrayList();
// add listener to track changes to list (does not contribute to problem)
people.addListener(
(ListChangeListener<? super Object>) c -> {
while (c.next()) {
if (c.wasReplaced()) {
System.out.printf(
"replaced 0x%08x %s with 0x%08x %s\n",
c.getRemoved().get(0).hashCode(),
c.getRemoved(),
c.getAddedSubList().get(0).hashCode(),
c.getAddedSubList());
} else if (c.wasAdded()) {
System.out.printf(
"added 0x%08x %s\n",
c.getAddedSubList().get(0).hashCode(),
c.getAddedSubList());
} else if (c.wasRemoved()) {
System.out.printf(
"removed 0x%08x %s\n",
c.getRemoved().hashCode(),
c.getRemoved());
}
}
});
// create sorted list of people by age/name
people.sorted(
(p1, p2) -> {
int equality = 0;
if (p1.getAge() != p2.getAge()) {
equality = p1.getAge() - p2.getAge();
} else {
equality = p1.getName().compareToIgnoreCase(p2.getName());
}
System.out.printf(
"return %d; [%s] %s [%s]\n",
equality,
p1,
equality < 0 ? "<" : equality > 0 ? ">" : "==",
p2);
return equality;
});
// add people to list
Arrays.asList("bob", "john", "joe", "ed", "fred")
.forEach(
n -> {
Person p = new Person(n);
// add the person to the list
people.add(p);
// listen for change to the person
p.data.addListener(
(MapChangeListener<? super String, ? super Object>) c -> {
// get the index of the person in the list
int index = people.indexOf(p);
if (c.wasRemoved() && c.wasAdded()) {
System.out.printf(
String.format(
"'%s' @ %d; '%s' changed from [%s] to [%s]\n",
p.getName(),
index,
c.getKey(),
c.getValueRemoved(),
c.getValueAdded()));
} else {
System.out.printf(
String.format(
"'%s' @ %d; '%s':[%s] %s\n",
p.getName(),
index,
c.getKey(),
c.wasAdded() ? c.getValueAdded() : c.getValueRemoved(),
c.wasAdded() ? "added" : "removed"));
}
System.out.printf("reset '%s' @ %d\n", p.getName(), index);
// **********************************************************************
// SOMETHING ABOUT THE PERSON CHANGED, SO RESET THE PERSON IN THE
// LIST TO TRIGGER AN UPDATE TO THE PERSON LIST
// **********************************************************************
people.set(index, p);
});
});
// randomly chose a person in the list and change their age to cause an update to the
// list of people
while (true) {
int index = (int) ((people.size() - 1) * Math.random());
System.out.println("--------------------------------------------------------");
System.out.printf("get person @ index %d\n", index);
Person p = people.get(index);
int age = (int) (Math.random() * 100);
System.out.printf("set age of '%s' @ index %d to %d\n", p.getName(), index, age);
p.setAge(age);
// **********************************************************************
// an exception in SortList will randomly occur in this loop
// **********************************************************************
}
}
}
---------- END SOURCE ----------
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
SortedList causes uncaught ArrayIndexOutOfBoundsException when resorting.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the code provided
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expect it not to throw uncatchable exception
ACTUAL -
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -6
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$15(TransformationList.java:106)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
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.set(ModifiableObservableListBase.java:163)
at test.collections.TestListUpdate.lambda$3(TestListUpdate.java:119)
at com.sun.javafx.collections.MapListenerHelper$SingleChange.fireValueChangedEvent(MapListenerHelper.java:163)
at com.sun.javafx.collections.MapListenerHelper.fireValueChangedEvent(MapListenerHelper.java:72)
at com.sun.javafx.collections.ObservableMapWrapper.callObservers(ObservableMapWrapper.java:115)
at com.sun.javafx.collections.ObservableMapWrapper.put(ObservableMapWrapper.java:173)
at test.collections.TestListUpdate$Person.setAge(TestListUpdate.java:22)
at test.collections.TestListUpdate.main(TestListUpdate.java:136)
ERROR MESSAGES/STACK TRACES THAT OCCUR :
added 0x1e80bfe8 [bob, 0yo]
added 0x50040f0c [john, 0yo]
return -8; [bob, 0yo] < [john, 0yo]
added 0x2dda6444 [joe, 0yo]
return -8; [bob, 0yo] < [joe, 0yo]
return 3; [john, 0yo] > [joe, 0yo]
added 0x5e9f23b4 [ed, 0yo]
return 5; [joe, 0yo] > [ed, 0yo]
return -3; [bob, 0yo] < [ed, 0yo]
added 0x4783da3f [fred, 0yo]
return -1; [ed, 0yo] < [fred, 0yo]
return 4; [joe, 0yo] > [fred, 0yo]
--------------------------------------------------------
get person @ index 3
set age of 'ed' @ index 3 to 73
'ed' @ 3; 'age':[73] added
reset 'ed' @ 3
replaced 0x5e9f23b4 [ed, 73yo] with 0x5e9f23b4 [ed, 73yo]
return -73; [fred, 0yo] < [ed, 73yo]
return -73; [joe, 0yo] < [ed, 73yo]
return -73; [john, 0yo] < [ed, 73yo]
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -6
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$15(TransformationList.java:106)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
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.set(ModifiableObservableListBase.java:163)
at test.collections.TestListUpdate.lambda$3(TestListUpdate.java:119)
at com.sun.javafx.collections.MapListenerHelper$SingleChange.fireValueChangedEvent(MapListenerHelper.java:163)
at com.sun.javafx.collections.MapListenerHelper.fireValueChangedEvent(MapListenerHelper.java:72)
at com.sun.javafx.collections.ObservableMapWrapper.callObservers(ObservableMapWrapper.java:115)
at com.sun.javafx.collections.ObservableMapWrapper.put(ObservableMapWrapper.java:173)
at test.collections.TestListUpdate$Person.setAge(TestListUpdate.java:22)
at test.collections.TestListUpdate.main(TestListUpdate.java:136)
REPRODUCIBILITY :
This bug can be reproduced often.
---------- BEGIN SOURCE ----------
package test.collections;
import java.util.Arrays;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
public class TestListUpdate {
static class Person {
ObservableMap<String, Object> data = FXCollections.observableHashMap();
public Person(String name) {
setName(name);
}
void setName(String name) { data.put("name", name); }
String getName() { return (String) data.get("name"); }
void setAge(int age) { data.put("age", age); }
int getAge() { return (int) data.getOrDefault("age", 0); }
@Override public String toString() { return String.format("%s, %d", getName(), getAge()); }
}
public static void main(String[] args) {
// create observable list of people
ObservableList<Person> people = FXCollections.observableArrayList();
// add listener to track changes to list (does not contribute to problem)
people.addListener(
(ListChangeListener<? super Object>) c -> {
while (c.next()) {
if (c.wasReplaced()) {
System.out.printf(
"replaced 0x%08x %s with 0x%08x %s\n",
c.getRemoved().get(0).hashCode(),
c.getRemoved(),
c.getAddedSubList().get(0).hashCode(),
c.getAddedSubList());
} else if (c.wasAdded()) {
System.out.printf(
"added 0x%08x %s\n",
c.getAddedSubList().get(0).hashCode(),
c.getAddedSubList());
} else if (c.wasRemoved()) {
System.out.printf(
"removed 0x%08x %s\n",
c.getRemoved().hashCode(),
c.getRemoved());
}
}
});
// create sorted list of people by age/name
people.sorted(
(p1, p2) -> {
int equality = 0;
if (p1.getAge() != p2.getAge()) {
equality = p1.getAge() - p2.getAge();
} else {
equality = p1.getName().compareToIgnoreCase(p2.getName());
}
System.out.printf(
"return %d; [%s] %s [%s]\n",
equality,
p1,
equality < 0 ? "<" : equality > 0 ? ">" : "==",
p2);
return equality;
});
// add people to list
Arrays.asList("bob", "john", "joe", "ed", "fred")
.forEach(
n -> {
Person p = new Person(n);
// add the person to the list
people.add(p);
// listen for change to the person
p.data.addListener(
(MapChangeListener<? super String, ? super Object>) c -> {
// get the index of the person in the list
int index = people.indexOf(p);
if (c.wasRemoved() && c.wasAdded()) {
System.out.printf(
String.format(
"'%s' @ %d; '%s' changed from [%s] to [%s]\n",
p.getName(),
index,
c.getKey(),
c.getValueRemoved(),
c.getValueAdded()));
} else {
System.out.printf(
String.format(
"'%s' @ %d; '%s':[%s] %s\n",
p.getName(),
index,
c.getKey(),
c.wasAdded() ? c.getValueAdded() : c.getValueRemoved(),
c.wasAdded() ? "added" : "removed"));
}
System.out.printf("reset '%s' @ %d\n", p.getName(), index);
// **********************************************************************
// SOMETHING ABOUT THE PERSON CHANGED, SO RESET THE PERSON IN THE
// LIST TO TRIGGER AN UPDATE TO THE PERSON LIST
// **********************************************************************
people.set(index, p);
});
});
// randomly chose a person in the list and change their age to cause an update to the
// list of people
while (true) {
int index = (int) ((people.size() - 1) * Math.random());
System.out.println("--------------------------------------------------------");
System.out.printf("get person @ index %d\n", index);
Person p = people.get(index);
int age = (int) (Math.random() * 100);
System.out.printf("set age of '%s' @ index %d to %d\n", p.getName(), index, age);
p.setAge(age);
// **********************************************************************
// an exception in SortList will randomly occur in this loop
// **********************************************************************
}
}
}
---------- END SOURCE ----------
- duplicates
-
JDK-8087508 SortedList is fragile when Comparator is poorly implemented (ArrayIndexOutOfBoundsException)
-
- Closed
-