SortedList, identical elements + re-arranging

Cormac Redmond credmond at certak.com
Wed Jan 28 03:50:32 UTC 2026


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/20260128/43d70e52/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/20260128/43d70e52/sl-0001.gif>


More information about the openjfx-dev mailing list