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