Hi, I had and still have the same problem in a similar situation.
The situation is as follows. A table contains a list of entries. A user selects an entry and performs a copy and paste operation (in order to modify the entry later). In this situation, the copied entry is identical by nature (Comparator returns 0). Even if I insert the *new* entry into the underlying list after the existing entry, SortedList decides to display the entry *before* the existing entry. This is confusing for a user. If there is a good solution for this, it would be helpful. However, I agree with Cormac Redmond that, ideally, the order should not change if the Comparator returns 0 (zero). Thanks, -- Daniel On Wed, Jan 28, 2026 at 4:51 AM Cormac Redmond <[email protected]> wrote: > Hi, > > I have noticed a troublesome quirk with SortedList which causes unexpected > re-positioning of elements in its list. > > This is a noticeable problem when you are, for example, sorting a > TableView on a column and re-insert an identical element at the same index > in the source list (i.e., nothings changing). E.g., if your goal is to > refresh a single row, even when there's no difference in the underlying > object, SortedList can still shift its place in its arrays and hence the > row shifts visibly in the table. On large tables, this can be a huge jump, > outside of view. This occurs when the sort column value shares the same > value with other rows (i.e., in my code sample below, multiple people are > aged 62 and sorting is by age). > > Sample below code and output below. The bit in green represents what I > would expect on each subsequent printout, but the bit in red shows the > random re-arrangement of elements. > > There's no need to include a full TableView example to show this. But I've > included a small gif too, for example. > > ---- Source Initial ---- > Person item [0]: Person[name=Bob, age=60, id=0] > Person item [1]: Person[name=Alice, age=70, id=1] > Person item [2]: Person[name=Diana, age=30, id=2] > Person item [3]: Person[name=Frank1, age=62, id=3] > Person item [4]: Person[name=Eve, age=62, id=4] > Person item [5]: Person[name=Frank2, age=62, id=5] > Person item [6]: Person[name=Jim, age=62, id=6] > Person item [7]: Person[name=Ivy, age=62, id=7] > Person item [8]: Person[name=Jack, age=53, id=8] > > ---- Sorted Initial (Subsequent Prints Should Match This, But Don't) ---- > Person item [0]: Person[name=Diana, age=30, id=2] > Person item [1]: Person[name=Jack, age=53, id=8] > Person item [2]: Person[name=Bob, age=60, id=0] > Person item [3]: Person[name=Frank1, age=62, id=3] > Person item [4]: Person[name=Eve, age=62, id=4] > Person item [5]: Person[name=Frank2, age=62, id=5] > Person item [6]: Person[name=Jim, age=62, id=6] > Person item [7]: Person[name=Ivy, age=62, id=7] > Person item [8]: Person[name=Alice, age=70, id=1] > > ---- Sorted After Identical Replace Index 5 ---- > Person item [0]: Person[name=Diana, age=30, id=2] > Person item [1]: Person[name=Jack, age=53, id=8] > Person item [2]: Person[name=Bob, age=60, id=0] > Person item [3]: Person[name=Frank2, age=62, id=5] > Person item [4]: Person[name=Frank1, age=62, id=3] > Person item [5]: Person[name=Eve, age=62, id=4] > Person item [6]: Person[name=Jim, age=62, id=6] > Person item [7]: Person[name=Ivy, age=62, id=7] > Person item [8]: Person[name=Alice, age=70, id=1] > > ---- Sorted After Identical Replace Index 4 ---- > Person item [0]: Person[name=Diana, age=30, id=2] > Person item [1]: Person[name=Jack, age=53, id=8] > Person item [2]: Person[name=Bob, age=60, id=0] > Person item [3]: Person[name=Eve, age=62, id=4] > Person item [4]: Person[name=Frank2, age=62, id=5] > Person item [5]: Person[name=Frank1, age=62, id=3] > Person item [6]: Person[name=Jim, age=62, id=6] > Person item [7]: Person[name=Ivy, age=62, id=7] > Person item [8]: Person[name=Alice, age=70, id=1] > > ---- Sorted After Identical Replace Index 3 ---- > Person item [0]: Person[name=Diana, age=30, id=2] > Person item [1]: Person[name=Jack, age=53, id=8] > Person item [2]: Person[name=Bob, age=60, id=0] > Person item [3]: Person[name=Frank1, age=62, id=3] > Person item [4]: Person[name=Eve, age=62, id=4] > Person item [5]: Person[name=Frank2, age=62, id=5] > Person item [6]: Person[name=Jim, age=62, id=6] > Person item [7]: Person[name=Ivy, age=62, id=7] > Person item [8]: Person[name=Alice, age=70, id=1] > > ...etc. > > > Code: > public class SortedListBehaviour { > > public static void main(String[] args) { > > ObservableList<Person> personList = > FXCollections.observableArrayList(); > > personList.addAll( > new Person("Bob", 60, 0), > new Person("Alice", 70, 1), > new Person("Diana", 30, 2), > new Person("Frank1", 62, 3), > new Person("Eve", 62, 4), > new Person("Frank2", 62, 5), > new Person("Jim", 62, 6), > new Person("Ivy", 62, 7), > new Person("Jack", 53, 8) > ); > > SortedList<Person> sortedList = new SortedList<>(personList, > Comparator.comparing(Person::age)); > > // Starting out > printList(personList, "Source Initial"); > > // This is sorted by age, all subsequent prints should look like > this, but they don't > printList(sortedList, "Sorted Initial (Subsequent Prints Should > Match This, But Don't)"); > > personList.set(5, new Person("Frank2", 62, 5)); // Replace with > identical, at same index > printList(sortedList, "Sorted After Identical Replace Index 5"); > > personList.set(4, new Person("Eve", 62, 4)); // Replace with > identical, at same index > printList(sortedList, "Sorted After Identical Replace Index 4"); > > personList.set(3, new Person("Frank1", 62, 3)); // Replace with > identical, at same index > printList(sortedList, "Sorted After Identical Replace Index 3"); > } > > private static void printList(final List<Person> list, final String > source) { > System.out.println("\n---- " + source + " ----"); > for (int i = 0; i < list.size(); i++) { > final Person next = list.get(i); > System.out.println("Person item [" + i + "]: " + next); > } > } > > public record Person(String name, int age, int id) { > @Override > public boolean equals(final Object o) { > if (o == null || getClass() != o.getClass()) { > return false; > } > final Person person = (Person) o; > return id == person.id && age == person.age && > Objects.equals(name, person.name); > } > > @Override > public int hashCode() { > return Objects.hash(name, age, id); > } > } > } > > > UI example also; the "Refresh" item here replaces the object with an > identical object and the same index, yet it will often jump around the > table as you can see, and selection can change too. > > [image: sl.gif] > > I understand that strictly speaking the list is still sorted correctly (by > age), but SortedList could make some effort to reduce re-arranging > already-present elements. > > > > Regards, > > *Cormac Redmond* > > > >
