SortedList, identical elements + re-arranging
Daniel Peintner
daniel.peintner at gmail.com
Wed Feb 4 07:44:03 UTC 2026
Hi John,
Thank you very much for your feedback.
> The solution IMHO is to make this simply crystal clear in your
> Comparator. If equal items should be ordered in insertion order, then the
> index should be part of the comparator. Any decision that SortedList makes
> could be correct for some use cases, and incorrect for others.
>
You might be right that one could (should?) of course take into account the
original order of the underlying list in the comparator to be sure.
On the other hand, the sort method of Collections [1] states
> This sort is guaranteed to be stable: equal elements will not be
reordered as a result of the sort.
I am not saying it is a *real* conflict, but my assumption/expectation was
that the order of a SortedList behaves somewhat the same.
Thanks,
-- Daniel
[1]
https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Collections.html#sort(java.util.List)
> --John
>
> On 29/01/2026 11:09, Daniel Peintner wrote:
>
> 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/20260204/d3fe7d5d/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/20260204/d3fe7d5d/sl-0001.gif>
More information about the openjfx-dev
mailing list