SortedList, identical elements + re-arranging

John Hendrikx john.hendrikx at gmail.com
Thu Jan 29 13:06:59 UTC 2026


Isn't this a different case? If I understood correctly, you expect a new
entry to be added below an existing entry that happens to be equal?

Basically, you expect the Table to act as a list sorted by index (you're
inserting at a the current selected index + 1 in unsorted space) for
copy/paste operations, while at the same time expecting the table to be
sorted on some key that is not its index (and doesn't include the index
in the comparator).

So while the copy/paste indeed occurs below the existing entry (in index
sorted view), the sorted view is completely unaware of this, and only
sees a new entry.

The SortedList could of course insert this new item after any existing
items, but what if the user selected "insert before" or some such
option?  Or what if the list was sorted in reverse?  Does that change
where the paste ends up (above or below still)?   What if there are 5
rows with the same value, should it insert it after some "selected row"
that it doesn't know about?

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.

--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 <http://person.id> && age ==
>     person.age && Objects.equals(name, person.name <http://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.
>
>     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/27cefe11/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/27cefe11/sl-0001.gif>


More information about the openjfx-dev mailing list