SortedList, identical elements + re-arranging

Daniel Peintner daniel.peintner at gmail.com
Thu Jan 29 10:09:59 UTC 2026


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 <credmond at certak.com> 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*
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20260129/6ccf3647/attachment-0001.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: sl.gif
Type: image/gif
Size: 58742 bytes
Desc: not available
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20260129/6ccf3647/sl-0001.gif>


More information about the openjfx-dev mailing list