2020/07/15

[筆記] Effective Java #14 考慮實作 Comparable 介面

Effective Java 3rd 簡體中文版筆記 #14 考慮實現 Comparable 介面
compareTo 是 java.lang.Comparable 介面中的唯一方法,若陣列內的元素皆有實現 Comparable,那麼就能用 Arrays.sort 來排序,同時亦可以與 collection 下的資料結構來進行協作。

實作 compareTo 時需要注意幾點:(經 sgn 方法處理後結果為 1、0 或 -1)
  1. sgn(x.compareTo(y)) == -sgn(y.compareTo(x)),如果 x.compareTo(y) 丟出 exception,那 y.compareTo(x) 亦會丟出 exception。
  2. 保證遞移性,當 x.compareTo(y) > 0 && y.compareTo(z) > 0 時,表示 x.compareTo(z) > 0。
  3. x.compareTo(y) == 0 時,sgn(x.compareTo(z)) 須與 sgn(y.compareTo(z)) 相同。
  4. 強烈建議 x.compareTo(y) == 0 時, x.equals(y) 為 true。
compareTo 無法對不同的 class 作比較,如果遇到類似情境應丟出 ClassCastException。違反 compareTo 的規定,會導致調用它的相關資料結構受到影響,像是 TreeSetTreeMapCollections 等等。第 4 項是建議, 並非真正的規定,那如果 equalscompareTo 的結果不一致,會怎麼樣? 像是 java.math.BigDecimal 就是上述的例子,在使用 HashSetTreeSet 就會有不一樣的結果。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("1.00");
System.out.println("a.equals(b) is " + a.equals(b));               // false
System.out.println("a.compareTo(b) is " + (a.compareTo(b) == 0));  // true

Set<BigDecimal> hashSet = new HashSet<>();
hashSet.add(a);
hashSet.add(b);
System.out.println(hashSet.size());  // 2
Set<BigDecimal> treeSet = new TreeSet<>();
treeSet.add(a);
treeSet.add(b);
System.out.println(treeSet.size());  // 1
compareTo 裡使用 < 及 > 是容易混淆的,建議使用 Short.compare 或 Double.compare 等方法。Java 8 提出 Comparator 介面,它包含相當多比較方法,像是 thenComparingthenComparingInt,十分方便。有關 Comparator 及 Lambda 等用法,後續有機會再介紹。
// java.util.Comparator source code
default Comparator<T> thenComparing(Comparator<? super T> other) {
    Objects.requireNonNull(other);
    return (Comparator<T> & Serializable) (c1, c2) -> {
        int res = compare(c1, c2);
        return (res != 0) ? res : other.compare(c1, c2);
    };
}

// comparator 
private static final Comparator COMPARATOR =
    Comparator.comparing(Seat::getRoom).thenComparing(Seat::getDesk);
轉載請註明原文網址 https://cookieandcoketw.blogspot.com/2020/07/effective-java-14-comparable.html

沒有留言:

張貼留言