有人可以用簡單的術語來解釋我,爲什麼這段代碼拋出一個異常,「比較方法違反了它的一般合同!」,我該如何解決?「比較方法違反其總合同!」
private int compareParents(Foo s1, Foo s2) {
if (s1.getParent() == s2) return -1;
if (s2.getParent() == s1) return 1;
return 0;
}
有人可以用簡單的術語來解釋我,爲什麼這段代碼拋出一個異常,「比較方法違反了它的一般合同!」,我該如何解決?「比較方法違反其總合同!」
private int compareParents(Foo s1, Foo s2) {
if (s1.getParent() == s2) return -1;
if (s2.getParent() == s1) return 1;
return 0;
}
您的比較器不是傳遞性的。
設A
是B
父,和B
是C
父。既然A > B
和B > C
,那麼它一定是A > C
。但是,如果在A
和C
上調用比較器,它將返回零,意味着A == C
。這違反了合同,因此拋出了異常。
這是相當不錯的圖書館檢測到這一點,讓你知道,而不是行爲不正常。
滿足compareParents()
中傳遞性要求的一種方法是遍歷getParent()
鏈,而不是僅查看直接祖先。
您無法比較像這樣的對象數據:s1.getParent() == s2
- 這將比較對象引用。你應該重寫equals function
Foo的類,然後他們這樣s1.getParent().equals(s2)
不,實際上我認爲OP正在嘗試對某種列表進行排序,並且希望實際比較引用。 –
比較只是因爲這是我得到了什麼,當我用Google搜索這個錯誤,我的問題是,我有
if (value < other.value)
return -1;
else if (value >= other.value)
return 1;
else
return 0;
的value >= other.value
應該(顯然)實際上是value > other.value
,這樣你就可以用相同的對象返回0。
我必須補充說,如果你的任何'value'是一個NaN(如果'value'是一個'double'或'float'),那麼它也會失敗。 – Matthieu
我已經看到這種情況發生在一段代碼,在其中執行空值經常重複檢查:
if((A==null) && (B==null)
return +1;//WRONG: two null values should return 0!!!
合同的違反往往意味着比較不提供正確或一致的比較對象時的值。例如,您可能要執行字符串比較,並迫使空字符串進行排序與結尾:
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);
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));
}
}
}
在我們的例子,因爲我們不小心翻的比較順序分別收到此錯誤s1和s2。所以要注意這一點。這顯然比下面的方式更復雜,但是這是一個例證:
s1 == s2
return 0;
s2 > s1
return 1;
s1 < s2
return -1;
即使你的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;
}
在我來說,我在做類似如下:
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爲空。
並引發異常。 –
我對Java或Java比較API不太瞭解,但這種比較方法似乎錯誤。假設's1'是's2'的父親,'s2'不是's1'的父親。然後'compareParents(s1,s2)'爲'0',但'compareParents(s2,s1)'爲'1'。這沒有意義。 (另外,它不是傳遞性的,就像下面提到的aix一樣)。 – mquander
這個錯誤似乎只能由特定的庫生成http://cr.openjdk.java.net/~martin/webrevs/openjdk7/timsort/src/share /class/java/util/TimSort.java.html –