<div dir="ltr"><div dir="ltr"><div>Hi John,</div><div><br></div><div>Thank you very much for your feedback.</div></div><div class="gmail_quote gmail_quote_container"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><p>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.<br></p></div></blockquote><div><br></div>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.<br><br>On the other hand, the sort method of Collections [1] states<br><br>> This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.</div><div class="gmail_quote gmail_quote_container"><br></div><div class="gmail_quote gmail_quote_container">I am not saying it is a *real* conflict, but my assumption/expectation was that the order of a SortedList behaves somewhat the same.</div><div class="gmail_quote gmail_quote_container"><div><br></div><div>Thanks,</div><div><br></div><div>-- Daniel</div><div><br></div><div>[1] <a href="https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Collections.html#sort(java.util.List)">https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Collections.html#sort(java.util.List)</a></div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><p>
</p>
--John<br>
<br>
<div>On 29/01/2026 11:09, Daniel Peintner
wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">Hi,<br>
<br>
I had and still have the same problem in a similar situation. <br>
<br>
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).<br>
<div>In this situation, the copied entry is identical by nature
(Comparator returns 0).</div>
<div><br>
</div>
<div>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.</div>
<br>
This is confusing for a user.<br>
<br>
<div>If there is a good solution for this, it would be helpful.</div>
<div>However, I agree with Cormac Redmond that, ideally, the
order should not change if the Comparator returns 0 (zero).</div>
<br>
Thanks,<br>
<br>
<div>-- Daniel</div>
<div><br>
</div>
<div><br>
</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Wed, Jan 28, 2026 at
4:51 AM Cormac Redmond <<a href="mailto:credmond@certak.com" target="_blank">credmond@certak.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div dir="ltr">
<div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">Hi,</div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"><br>
</div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">I
have noticed a troublesome quirk with SortedList which
causes unexpected re-positioning of elements in its
list.</div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"><br>
</div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">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).</div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"><br>
</div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">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.</div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"><br>
</div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">There's
no need to include a full TableView example to show
this. But I've included a small gif too, for example.</div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"><br>
</div>
<div style="font-size:small;color:rgb(0,0,0)"><font face="monospace"><span style="color:rgb(34,34,34)">----
Source Initial ----</span></font></div>
<font face="monospace">Person item [0]: Person[name=Bob,
age=60, id=0]<br>
Person item [1]: Person[name=Alice, age=70, id=1]<br>
Person item [2]: Person[name=Diana, age=30, id=2]<br>
Person item [3]: Person[name=Frank1, age=62, id=3]<br>
Person item [4]: Person[name=Eve, age=62, id=4]<br>
Person item [5]: Person[name=Frank2, age=62, id=5]<br>
Person item [6]: Person[name=Jim, age=62, id=6]<br>
Person item [7]: Person[name=Ivy, age=62, id=7]<br>
Person item [8]: Person[name=Jack, age=53, id=8]<br>
<br>
---- Sorted Initial (Subsequent Prints Should Match
This, But Don't) ----<br>
Person item [0]: Person[name=Diana, age=30, id=2]<br>
Person item [1]: Person[name=Jack, age=53, id=8]<br>
Person item [2]: Person[name=Bob, age=60, id=0]<br>
<span style="background-color:rgb(0,255,0)"><span class="gmail_default" style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"></span>Person
item [3]: Person[name=Frank1, age=62, id=3]<br>
Person item [4]: Person[name=Eve, age=62, id=4]<br>
Person item [5]: Person[name=Frank2, age=62, id=5]</span><br>
Person item [6]: Person[name=Jim, age=62, id=6]<br>
Person item [7]: Person[name=Ivy, age=62, id=7]<br>
Person item [8]: Person[name=Alice, age=70, id=1]<br>
<br>
---- Sorted After Identical Replace Index 5 ----<br>
Person item [0]: Person[name=Diana, age=30, id=2]<br>
Person item [1]: Person[name=Jack, age=53, id=8]<br>
Person item [2]: Person[name=Bob, age=60, id=0]<br>
<span style="background-color:rgb(234,153,153)">Person
item [3]: Person[name=Frank2, age=62, id=5]<br>
Person item [4]: Person[name=Frank1, age=62, id=3]<br>
Person item [5]: Person[name=Eve, age=62, id=4]</span><br>
Person item [6]: Person[name=Jim, age=62, id=6]<br>
Person item [7]: Person[name=Ivy, age=62, id=7]<br>
Person item [8]: Person[name=Alice, age=70, id=1]<br>
<br>
---- Sorted After Identical Replace Index 4 ----<br>
Person item [0]: Person[name=Diana, age=30, id=2]<br>
Person item [1]: Person[name=Jack, age=53, id=8]<br>
Person item [2]: Person[name=Bob, age=60, id=0]<br>
<span style="background-color:rgb(234,153,153)">Person
item [3]: Person[name=Eve, age=62, id=4]<br>
Person item [4]: Person[name=Frank2, age=62, id=5]<br>
Person item [5]: Person[name=Frank1, age=62, id=3]</span><br>
Person item [6]: Person[name=Jim, age=62, id=6]<br>
Person item [7]: Person[name=Ivy, age=62, id=7]<br>
Person item [8]: Person[name=Alice, age=70, id=1]<br>
<br>
---- Sorted After Identical Replace Index 3 ----<br>
Person item [0]: Person[name=Diana, age=30, id=2]<br>
Person item [1]: Person[name=Jack, age=53, id=8]<br>
Person item [2]: Person[name=Bob, age=60, id=0]<br>
<span style="background-color:rgb(234,153,153)">Person
item [3]: Person[name=Frank1, age=62, id=3]<br>
Person item [4]: Person[name=Eve, age=62, id=4]<br>
Person item [5]: Person[name=Frank2, age=62, id=5]</span><br>
Person item [6]: Person[name=Jim, age=62, id=6]<br>
Person item [7]: Person[name=Ivy, age=62, id=7]<br>
Person item [8]: Person[name=Alice, age=70, id=1]</font><br>
<br clear="all">
</div>
<div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">...etc.</div>
<br>
</div>
<div><br>
</div>
<div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">Code:</div>
<div style="font-size:small;color:rgb(0,0,0)"><font face="monospace">public class SortedListBehaviour {<br>
<br>
public static void main(String[] args) {<br>
<br>
ObservableList<Person> personList =
FXCollections.observableArrayList();<br>
<br>
personList.addAll(<br>
new Person("Bob", 60, 0),<br>
new Person("Alice", 70, 1),<br>
new Person("Diana", 30, 2),<br>
new Person("Frank1", 62, 3),<br>
new Person("Eve", 62, 4),<br>
new Person("Frank2", 62, 5),<br>
new Person("Jim", 62, 6),<br>
new Person("Ivy", 62, 7),<br>
new Person("Jack", 53, 8)<br>
);<br>
<br>
SortedList<Person> sortedList = new
SortedList<>(personList,
Comparator.comparing(Person::age));<br>
<br>
// Starting out<br>
printList(personList, "Source Initial");<br>
<br>
// This is sorted by age, all subsequent
prints should look like this, but they don't<br>
printList(sortedList, "Sorted Initial
(Subsequent Prints Should Match This, But Don't)");<br>
<br>
personList.set(5, new Person("Frank2", 62,
5)); // Replace with identical, at same index<br>
printList(sortedList, "Sorted After Identical
Replace Index 5");<br>
<br>
personList.set(4, new Person("Eve", 62, 4));
// Replace with identical, at same index<br>
printList(sortedList, "Sorted After Identical
Replace Index 4");<br>
<br>
personList.set(3, new Person("Frank1", 62,
3)); // Replace with identical, at same index<br>
printList(sortedList, "Sorted After Identical
Replace Index 3");<br>
}<br>
<br>
private static void printList(final
List<Person> list, final String source) {<br>
System.out.println("\n---- " + source + "
----");<br>
for (int i = 0; i < list.size(); i++) {<br>
final Person next = list.get(i);<br>
System.out.println("Person item [" + i +
"]: " + next);<br>
}<br>
}<br>
<br>
public record Person(String name, int age, int id)
{<br>
@Override<br>
public boolean equals(final Object o) {<br>
if (o == null || getClass() !=
o.getClass()) {<br>
return false;<br>
}<br>
final Person person = (Person) o;<br>
return id == <a href="http://person.id" target="_blank">person.id</a>
&& age == person.age &&
Objects.equals(name, <a href="http://person.name" target="_blank">person.name</a>);<br>
}<br>
<br>
@Override<br>
public int hashCode() {<br>
return Objects.hash(name, age, id);<br>
}<br>
}<br>
}</font><br>
</div>
<div style="font-size:small;color:rgb(0,0,0)"><font face="monospace"><br>
</font></div>
<div style="font-size:small;color:rgb(0,0,0)"><font face="monospace"><br>
</font></div>
<div style="font-size:small;color:rgb(0,0,0)"><font face="verdana, sans-serif">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.</font></div>
<div style="font-size:small;color:rgb(0,0,0)"><font face="monospace"><br>
</font></div>
<div style="font-size:small;color:rgb(0,0,0)"><img src="cid:ii_19c27959ec8ca45b2af1" alt="sl.gif" width="562" height="422"><br>
</div>
<br>
</div>
<div>
<div style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">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.</div>
<br>
</div>
<div><br>
</div>
<div>
<div dir="ltr" class="gmail_signature">
<div dir="ltr">
<div><font face="verdana, sans-serif" color="#000000"><br>
</font></div>
<div><font face="verdana, sans-serif" color="#000000">Regards,</font></div>
<div><font face="verdana, sans-serif" color="#000000"><b><br>
</b></font></div>
<font face="verdana, sans-serif" color="#000000"><b>Cormac
Redmond</b></font>
<div><br>
</div>
<div><br>
</div>
<div><br>
</div>
</div>
</div>
</div>
</div>
</blockquote>
</div>
</blockquote>
</div>
</blockquote></div></div>