<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Hi Daniel,</p>
    <div class="moz-cite-prefix">On 04/02/2026 08:44, Daniel Peintner
      wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:CAAoP7T6DhEN8pDxhTDguw53j_ApppbVYwDec1_MuNXxdp7X_RQ@mail.gmail.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <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>
    </blockquote>
    <p>This statement is about the sort algorithm used.  It means that
      if you have two equal elements (already present at the time of
      sorting) that the algorithm won't decide to swap them around.  Some
      algorithms may reorder equal elements unintentionally due to the
      way they are implemented.  This re-arranging for such algorithms
      could occur on each call to `sort`, even if nothing else changed. 
      Stable sort algorithms take extra care to ensure unnecessary
      re-arranging of equal elements doesn't happen. For example, given
      5 equal elements:</p>
    <p>      A1, A2, A3, A4, A5</p>
    <p>A stable sort when re-applied will never re-arrange these,
      however, an unstable sort may re-arrange these in some random
      order each time the list is sorted, for example to:</p>
    <p>      A2, A4, A1, A5, A3<br>
    </p>
    <p>The stability statement however provides no guarantees when you
      add a new element (even if it was the same as one that was just
      removed, or duplicated).  In that case the new element gets a
      random spot among its peers.  Only in subsequent sorts does the
      algorithm guarantee that it doesn't move relative to other equal
      elements; so if I add A6, it may end up anywhere (the rest of the
      elements however remain in the same relative order):</p>
    <p>     A1, A2, A6, A3, A4, A5</p>
    <p>Only after a resort does it ensure again that the sort is stable,
      and it will remain in the same order:<br>
    </p>
    <p>     A1, A2, A6, A3, A4, A5</p>
    <p>If I remove any of the elements, a stable sort will also ensure
      that the remaining equal elements stay in the same order, so if I
      remove A4, it will become:</p>
    <p>     A1, A2, A6, A3, A5</p>
    <p>I hope that clarifies it a bit.<br>
    </p>
    <p></p>
    <blockquote type="cite"
cite="mid:CAAoP7T6DhEN8pDxhTDguw53j_ApppbVYwDec1_MuNXxdp7X_RQ@mail.gmail.com">
      <div dir="ltr"><br>
        <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>
    </blockquote>
    <p>Unfortunately, it can't guarantee this, and the stability
      guarantee doesn't apply in your case.  So if you really want the
      new / duplicate item to appear in a specific location relative to
      other equal elements, you must add another sort key to the
      comparator to remove any ambiguity.<br>
    </p>
    <p>--John<br>
    </p>
    <blockquote type="cite"
cite="mid:CAAoP7T6DhEN8pDxhTDguw53j_ApppbVYwDec1_MuNXxdp7X_RQ@mail.gmail.com">
      <div dir="ltr">
        <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)"
              moz-do-not-send="true" class="moz-txt-link-freetext">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"
                      moz-do-not-send="true"
                      class="moz-txt-link-freetext">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"
                              moz-do-not-send="true">person.id</a>
                            && age == person.age &&
                            Objects.equals(name, <a
                              href="http://person.name" target="_blank"
                              moz-do-not-send="true">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:part1.e9bHUM0X.JsPe3oIw@gmail.com"
                            alt="sl.gif" class="" 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>
    </blockquote>
  </body>
</html>