2013-03-22 17 views
3

有人可以說明compareTo()與類的equals()不一致時的後果。我已經讀過,如果Obj1.compareTo(Obj2) = 0那麼它不是強制性的是Obj1.equals(Obj2) = true。但是如果發生這種情況會有什麼後果。謝謝。當compareTo()與equals()不一致時的後果

+0

除非你做一個 – 2013-03-22 14:30:10

+0

@Sam我,你才能取悅更多煞費苦心。 – Trying 2013-03-22 14:31:19

+1

這個問題非常模糊。結果是「如果你用兩種不同的方式比較它們,你會得到兩個不同的答案」 - 後續結果取決於你爲什麼要進行比較。 – 2013-03-22 14:32:07

回答

10

Comparable文檔解釋了這個在一些細節:

一類C的自然順序被認爲是與equals當且僅當e1.compareTo(e2) == 0對每一個e1相同的布爾值e1.equals(e2)一致, e2類別C。請注意,null不是任何類的實例,並且e.compareTo(null)應拋出NullPointerException,即使e.equals(null)返回false

強烈建議(儘管不要求)自然排序與equals一致。這是因爲,如果排序集(和排序映射)沒有顯式比較器,當它們與自然排序與equals不一致的元素(或鍵)一起使用時,其行爲會「奇怪」。特別是,這樣一個有序的集合(或有序的映射)違反了集合(或映射)的一般合約,這是根據equals方法定義的。

例如,如果加上兩個鍵ab使得(!a.equals(b) && a.compareTo(b) == 0)到有序集合不使用顯式的比較器,第二個加法操作返回false(有序集合不增加的大小)因爲ab等同於排序集的視角。

實際上,實現Comparable的幾乎所有Java核心類都具有與equals一致的自然排序。一個例外是java.math.BigDecimal,其自然排序等於BigDecimal具有相同值和不同精度的對象(如4.04.00)。

+0

比較'compareTo'與'equals'不一致的例子:http://stackoverflow.com/questions/12587896/customizing-the-get-method-in-hashmap/12588005#12588005 – assylias 2013-03-22 14:42:19

+0

@NPE你能不能請解釋一下這個「特別是,這樣一個有序集合(或排序映射)違反了集合(或映射)的一般契約,這是用等價方法定義的。」舉一個例子。我無法正確地得到它。謝謝。 – Trying 2013-03-22 14:42:35

+2

@Trying:從Set的文檔:*集合不包含元素e1和e2的對,使得e1.equals(e2)*。另一方面,'SortedSet'(它必須完成這個合約,因爲它是一個'Set')使用'compareTo()'而不是'equals()'。因此,如果你的對象不一致地實現'compareTo/equals',並且把它們放在一個'SortedSet'中,那麼你就會迫使後者違反合同。 – NPE 2013-03-22 14:53:00

0

一些收藏會假設如果兩個物體跟隨obj1.compareTo(obj2) = 0然後obj1.equals(obj2)也是如此。例如:排序爲TreeSet。 未能符合此邏輯將導致圖標一致的集合。參見: Comparator and equals()

3

儘管文檔說明一致性不是強制性的,但最好始終確保一致性,因爲您永遠不知道您的對象是否會在TreeMap/TreeSet之類的某一天出現。如果compareTo()對於不相等的2個對象返回0,則所有基於Tree的集合都將被打破。

例如,假設一類Query,執行一個SQL查詢,有2場:

  • tableList:表
  • 參考清單:使用這樣的查詢程序列表

假設兩個對象在它們的tableList相等時是相等的,即tableList是這個對象的天然關鍵。 hashCode()equals()只考慮現場tableList

public class Query implements Comparable { 
    List<String> tableList; 
    List<String> references; 

    Query(List<String> tableList, List<String> references) { 
     this.tableList = tableList; 
     this.references = references; 
     Collections.sort(tableList); // normalize 
    } 

    @Override 
    public int hashCode() { 
     int hash = 5; 
     hash = 53 * hash + Objects.hashCode(this.tableList); 
     return hash; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (obj == null) { 
      return false; 
     } 
     if (getClass() != obj.getClass()) { 
      return false; 
     } 
     final Query other = (Query) obj; 
     return Objects.equals(this.tableList, other.tableList); 
    } 
} 

比方說,我們想排序是沿着引用的數量。 天真地編寫代碼產生了compareTo()方法,這可能是這樣的:

public int compareTo(Object o) { 
    Query other = (Query) o; 
    int s1 = references.size(); 
    int s2 = other.references.size(); 
    if (s1 == s2) { 
     return 0; 
    } 
    return s1 - s2; 
} 

這樣做似乎平等確定和排序是在兩個不同的領域完成的,越遠越好。

但是,無論何時放入TreeSetTreeMap,都是災難性的:這些類的實現認爲如果compareTo返回0,則元素相等。在這種情況下,這意味着具有相同參考數量的每個對象確實是「相等的」對象,顯然不是這種情況。

更好compareTo()方法可能是:

public int compareTo(Object o) { 
    Query other = (Query) o; 
    // important to match equals!!! 
    if (this.equals(other)) { 
     return 0; 
    } 
    int s1 = references.size(); 
    int s2 = other.references.size(); 
    if (s1 == s2) { 
     return -1; // not 0, they are NOT equal! 
    } 
    return s1 - s2; 
} 
相關問題