2011-11-30 89 views
123

有人可以用簡單的術語來解釋我,爲什麼這段代碼拋出一個異常,「比較方法違反了它的一般合同!」,我該如何解決?「比較方法違反其總合同!」

private int compareParents(Foo s1, Foo s2) { 
    if (s1.getParent() == s2) return -1; 
    if (s2.getParent() == s1) return 1; 
    return 0; 
} 
+0

並引發異常。 –

+2

我對Java或Java比較API不太瞭解,但這種比較方法似乎錯誤。假設's1'是's2'的父親,'s2'不是's1'的父親。然後'compareParents(s1,s2)'爲'0',但'compareParents(s2,s1)'爲'1'。這沒有意義。 (另外,它不是傳遞性的,就像下面提到的aix一樣)。 – mquander

+4

這個錯誤似乎只能由特定的庫生成http://cr.openjdk.java.net/~martin/webrevs/openjdk7/timsort/src/share /class/java/util/TimSort.java.html –

回答

189

您的比較器不是傳遞性的。

AB父,和BC父。既然A > BB > C,那麼它一定是A > C。但是,如果在AC上調用比較器,它將返回零,意味着A == C。這違反了合同,因此拋出了異常。

這是相當不錯的圖書館檢測到這一點,讓你知道,而不是行爲不正常。

滿足compareParents()中傳遞性要求的一種方法是遍歷getParent()鏈,而不是僅查看直接祖先。

+3

在Java 7的'java.util.Arrays.sort'中引入http://stackoverflow.com/questions/7849539/comparison-method-violates-its-general-contract-java-7-only – leonbloy

+11

庫正在檢測的事實這太棒了。有人在陽光下值得拋出一個巨大的_You're歡迎。 – Qix

+5

@Qix我想也許感嘆號是他們說的那種方式。 –

0

您無法比較像這樣的對象數據:s1.getParent() == s2 - 這將比較對象引用。你應該重寫equals function Foo的類,然後他們這樣s1.getParent().equals(s2)

+0

不,實際上我認爲OP正在嘗試對某種列表進行排序,並且希望實際比較引用。 –

31

比較只是因爲這是我得到了什麼,當我用Google搜索這個錯誤,我的問題是,我有

if (value < other.value) 
    return -1; 
else if (value >= other.value) 
    return 1; 
else 
    return 0; 

value >= other.value應該(顯然)實際上是value > other.value,這樣你就可以用相同的對象返回0。

+5

我必須補充說,如果你的任何'value'是一個NaN(如果'value'是一個'double'或'float'),那麼它也會失敗。 – Matthieu

1

我已經看到這種情況發生在一段代碼,在其中執行空值經常重複檢查:

if((A==null) && (B==null) 
    return +1;//WRONG: two null values should return 0!!! 
18

合同的違反往往意味着比較不提供正確或一致的比較對象時的值。例如,您可能要執行字符串比較,並迫使空字符串進行排序與​​結尾:

if (one.length() == 0) { 
    return 1;     // empty string sorts last 
} 
if (two.length() == 0) { 
    return -1;     // empty string sorts last     
} 
return one.compareToIgnoreCase(two); 

但是這忽略其中兩個一和二是空的情況下 - 在這種情況下,錯誤的值(1而不是0來顯示匹配),並且比較器將其報告爲違規。它應該寫爲:

if (one.length() == 0) { 
    if (two.length() == 0) { 
     return 0;    // BOth empty - so indicate 
    } 
    return 1;     // empty string sorts last 
} 
if (two.length() == 0) { 
    return -1;     // empty string sorts last     
} 
return one.compareToIgnoreCase(two); 
3

Java不檢查嚴格意義上的一致性,只會在出現嚴重問題時通知您。它也不會給你提供錯誤信息。

我很困惑,與正在發生的事情我分揀機,並提出了嚴格的consistencyChecker,也許這將幫助你:

/** 
* @param dailyReports 
* @param comparator 
*/ 
public static <T> void checkConsitency(final List<T> dailyReports, final Comparator<T> comparator) { 
    final Map<T, List<T>> objectMapSmallerOnes = new HashMap<T, List<T>>(); 

    iterateDistinctPairs(dailyReports.iterator(), new IPairIteratorCallback<T>() { 
    /** 
    * @param o1 
    * @param o2 
    */ 
    @Override 
    public void pair(T o1, T o2) { 
     final int diff = comparator.compare(o1, o2); 
     if (diff < Compare.EQUAL) { 
     checkConsistency(objectMapSmallerOnes, o1, o2); 
     getListSafely(objectMapSmallerOnes, o2).add(o1); 
     } else if (Compare.EQUAL < diff) { 
     checkConsistency(objectMapSmallerOnes, o2, o1); 
     getListSafely(objectMapSmallerOnes, o1).add(o2); 
     } else { 
     throw new IllegalStateException("Equals not expected?"); 
     } 
    } 
    }); 
} 

/** 
* @param objectMapSmallerOnes 
* @param o1 
* @param o2 
*/ 
static <T> void checkConsistency(final Map<T, List<T>> objectMapSmallerOnes, T o1, T o2) { 
    final List<T> smallerThan = objectMapSmallerOnes.get(o1); 

    if (smallerThan != null) { 
    for (final T o : smallerThan) { 
     if (o == o2) { 
     throw new IllegalStateException(o2 + " cannot be smaller than " + o1 + " if it's supposed to be vice versa."); 
     } 
     checkConsistency(objectMapSmallerOnes, o, o2); 
    } 
    } 
} 

/** 
* @param keyMapValues 
* @param key 
* @param <Key> 
* @param <Value> 
* @return List<Value> 
*/ 
public static <Key, Value> List<Value> getListSafely(Map<Key, List<Value>> keyMapValues, Key key) { 
    List<Value> values = keyMapValues.get(key); 

    if (values == null) { 
    keyMapValues.put(key, values = new LinkedList<Value>()); 
    } 

    return values; 
} 

/** 
* @author Oku 
* 
* @param <T> 
*/ 
public interface IPairIteratorCallback<T> { 
    /** 
    * @param o1 
    * @param o2 
    */ 
    void pair(T o1, T o2); 
} 

/** 
* 
* Iterates through each distinct unordered pair formed by the elements of a given iterator 
* 
* @param it 
* @param callback 
*/ 
public static <T> void iterateDistinctPairs(final Iterator<T> it, IPairIteratorCallback<T> callback) { 
    List<T> list = Convert.toMinimumArrayList(new Iterable<T>() { 

    @Override 
    public Iterator<T> iterator() { 
     return it; 
    } 

    }); 

    for (int outerIndex = 0; outerIndex < list.size() - 1; outerIndex++) { 
    for (int innerIndex = outerIndex + 1; innerIndex < list.size(); innerIndex++) { 
     callback.pair(list.get(outerIndex), list.get(innerIndex)); 
    } 
    } 
} 
+1

這是如何工作的?你如何使用它? –

+0

只需使用參數列表和比較器調用checkConsitency metohod。 – Martin

+0

你的代碼不能編譯。類'比較','轉換'(可能還有其他)沒有定義。請用獨立示例更新代碼sniplet。 – Gili

5

在我們的例子,因爲我們不小心翻的比較順序分別收到此錯誤s1和s2。所以要注意這一點。這顯然比下面的方式更復雜,但是這是一個例證:

s1 == s2 
    return 0; 
s2 > s1 
    return 1; 
s1 < s2 
    return -1; 
6

即使你的compareTo是持有在理論上傳遞,有時微妙的錯誤胡來......如浮點運算錯誤。它發生在我身上。這是我的代碼:

public int compareTo(tfidfContainer compareTfidf) { 
    //descending order 
    if (this.tfidf > compareTfidf.tfidf) 
     return -1; 
    else if (this.tfidf < compareTfidf.tfidf) 
     return 1; 
    else 
     return 0; 

} 

傳遞屬性顯然成立,但由於某種原因,我得到了IllegalArgumentException。事實證明,由於浮點運算中的微小錯誤,導致傳遞屬性破壞的舍入錯誤不應該發生!所以我重寫了代碼考慮真的很小的差別0,和它的工作:

public int compareTo(tfidfContainer compareTfidf) { 
    //descending order 
    if ((this.tfidf - compareTfidf.tfidf) < .000000001) 
     return 0; 
    if (this.tfidf > compareTfidf.tfidf) 
     return -1; 
    else if (this.tfidf < compareTfidf.tfidf) 
     return 1; 
    return 0; 
} 
0

在我來說,我在做類似如下:

if (a.someField == null) { 
    return 1; 
} 

if (b.someField == null) { 
    return -1; 
} 

if (a.someField.equals(b.someField)) { 
    return a.someOtherField.compareTo(b.someOtherField); 
} 

return a.someField.compareTo(b.someField); 

什麼我忘了檢查是當兩個一.someField和b.someField爲空。

相關問題