<div dir="ltr"><div><div class="gmail_default" style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">Hi,</div><div class="gmail_default" style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"><br></div><div class="gmail_default" 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 class="gmail_default" style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"><br></div><div class="gmail_default" 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 class="gmail_default" style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"><br></div><div class="gmail_default" 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 class="gmail_default" style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"><br></div><div class="gmail_default" 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 class="gmail_default" style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)"><br></div><div class="gmail_default" 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 class="gmail_default" style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">...etc.</div><br></div><div><br></div><div><div class="gmail_default" style="font-family:verdana,sans-serif;font-size:small;color:rgb(0,0,0)">Code:</div><div class="gmail_default" 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">person.id</a> && age == person.age && Objects.equals(name, <a href="http://person.name">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 class="gmail_default" style="font-size:small;color:rgb(0,0,0)"><font face="monospace"><br></font></div><div class="gmail_default" style="font-size:small;color:rgb(0,0,0)"><font face="monospace"><br></font></div><div class="gmail_default" 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 class="gmail_default" style="font-size:small;color:rgb(0,0,0)"><font face="monospace"><br></font></div><div class="gmail_default" style="font-size:small;color:rgb(0,0,0)"><img src="cid:ii_mkxh94ei0" alt="sl.gif" width="562" height="422"><br></div><br></div><div><div class="gmail_default" 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" data-smartmail="gmail_signature"><div dir="ltr"><div><font color="#000000" face="verdana, sans-serif"><br></font></div><div><font color="#000000" face="verdana, sans-serif">Regards,</font></div><div><font color="#000000" face="verdana, sans-serif"><b><br></b></font></div><font color="#000000" face="verdana, sans-serif"><b>Cormac Redmond</b></font><div><br></div><div><br></div><div><br></div></div></div></div></div>