2010-03-13 43 views
0

我相信這個問題同樣適用於C#,因爲它們都需要{c,C} ompareTo與{e,E} quals一致:C#/ Java:正確實施CompareTo當Equals測試參考身份

假設我想我的equals()方法是相同的參考檢查,即:

public bool equals(Object o) { 
    return this == o; 
} 

在這種情況下,我該如何實現的compareTo(對象o)(或其仿製藥)?部分原因是容易的,但我不知道的另一部分:

public int compareTo(Object o) { 
    MyClass other = (MyClass)o; 
    if (this == other) { 
     return 0; 
    } else { 
     int c = foo.CompareTo(other.foo) 
     if (c == 0) { 
      // what here? 
     } else { 
      return c; 
     } 
    } 
} 

我不能只是一味地返回1或-1,因爲該解決方案應堅持的compareTo的正常需求。我可以檢查所有的實例字段,但是,如果他們都是平等的,我還是很喜歡的compareTo返回0以外的值應該是真實的,a.compareTo(二)== - (b.compareTo(一) ),只要對象的狀態沒有改變,順序應該保持一致。

我不關心整個虛擬機的調用訂貨,但是。這讓我覺得我可以使用類似內存地址的東西,如果我能夠做到的話。再說,也許這是行不通的,因爲垃圾收集器可以決定走動我的對象。

hashCode是另一種想法,但我想要的東西總是總是獨特的,而不僅僅是獨特的。

任何想法?

+0

我不明白。如果對象上沒有合理的*邏輯*排序,爲什麼你需要有一個排序呢?如果您的對象在邏輯上不能相互比較,請不要打擾實施Comparable。你想解決什麼問題? – Joren

+0

您可以使用Object.ReferenceEquals(a,b)來檢查它是否是完全相同的對象。 –

+0

@Joren:我的對象確實有一個邏輯順序,但是一旦我經歷了他們所有的狀態,當equals檢查引用相等時,我仍然需要compareTo與equals相等。 –

回答

3

首先,如果你正在使用Java 5或以上,你應該實現Comparable<MyClass>而不是普通的舊Comparable,因此您compareTo方法應採取MyClass類型,而不是Object的參數:

public int compareTo(MyClass other) { 
    if (this == other) { 
     return 0; 
    } else { 
     int c = foo.CompareTo(other.foo) 
     if (c == 0) { 
      // what here? 
     } else { 
      return c; 
     } 
    } 
} 

由於你的問題,喬希布洛赫有效的Java(第3章,第12項)的說:

實現程序必須確保SGN(則x.compareTo(Y))== -sgn(y.compare- 到(x ) )爲所有x和y。 (這意味着則x.compareTo(Y)當且僅當y.compareTo(X)拋出異常必須拋出異常 。)

這意味着,如果在上面的碼c == 0,則必須返回0。

這反過來意味着您可以擁有不相等的對象A和B,但它們的比較返回0.布洛赫先生對此有何評論?

強烈建議但不是嚴格要求(x.compareTo(y) == 0)==(x.equals(y))。一般來說,任何實現了Comparable接口並違反這個條件的類應該清楚地指出 這個事實。推薦的語言是「注意:此類具有天然 排序與equals不一致。」

而且

一類,它的compareTo方法強加秩序 與equals仍然會不一致但包含 類別的元素的排序集合可能不服從適當集合 接口(集合,集合或映射)的一般合同。這是因爲這些接口的一般合約 是根據equals方法定義的,但排序後的集合 使用由compareTo強加的相等性測試來代替等於。如果發生這種情況,這不是一場災難,但它是需要注意的事情。

更新:因此,與當前類恕我直言,你不能讓compareToequals一致。如果你確實需要,我看到的唯一方法就是引入一個新成員,這會給你的班級一個嚴格的自然排序。然後,如果兩個對象的所有有意義的字段與0比較,您仍然可以根據它們的特殊順序值來決定這兩個對象的順序。

此額外成員可能是實例計數器或創建時間戳。或者,您可以嘗試使用UUID

+0

我的問題正是我如何使compareTo與equals相等時,等於只是測試參考平等? –

+0

@ Paul A Jungwirth好吧,我明白了:-)請看我的更新。 –

+0

嗯,櫃檯和獨特的ID都是可能的想法。你知道C#是否有與Java的獨特ID相媲美的東西嗎? –

0

的仿製藥是容易對付在我看來,取決於你的外部需求是什麼,這是一個IComparable<MyClass>例如:

public int CompareTo(MyClass other) { 
    if (other == null) return 1; 
    if (this == other) { 
     return 0; 
    } else { 
     return foo.CompareTo(other.foo); 
    } 
} 

如果類是等於或者foo是平等的,這是結束的比較,除非有次要的排序,在這種情況下添加它作爲回報如果foo.CompareTo(other.foo) == 0

如果您的類有一個ID或什麼,然後比較作爲次要,否則不用擔心它。 ..他們存儲的集合,它是爲了達到這些班來比較是發生了什麼事,以確定相等的對象或等於object.foo值的情況下最終訂單。

1

兩個對象不需要被引用等同於處於相同的等價類中。在我看來,兩個不同的對象在比較中是相同的,但是不能等同。例如,對我來說,看起來非常自然,如果你從數據庫的同一行合併了兩個不同的對象,那麼它們將用於比較,但不會引用相同。

我實際上更傾向於修改平等的行爲,以反映他們如何比較而不是相反。對於我能想到的大多數目的而言,這會更自然。

2

在Java或C#中,一般來說,沒有固定的對象排序。垃圾收集器可以移動實例,同時執行您的compareTo或使用compareTo的排序操作。如你所述,散列碼通常不是唯一的,所以它們不可用(兩個具有相同散列碼的不同實例將使您回到原始問題)。許多人認爲表示對象id(MyObject @ 33c0d9d)的Java Object.toString實現只不過是對象的類名後跟哈希碼。據我所知,JVM和CLR都沒有實例id的概念。

如果你真的想要一個一致的類的順序,你可以嘗試爲你創建的每個新實例使用一個遞增的數字。請注意,增加此計數器必須是線程安全的,所以它會相對昂貴(在C#中,您可以使用Interlocked.Increment)。