有人可以用简单的语言解释我,为什么这段代码会抛出异常,“比较方法违反了它的一般合同!”,我该如何解决?
private int compareParents(Foo s1, Foo s2) {
if (s1.getParent() == s2) return -1;
if (s2.getParent() == s1) return 1;
return 0;
}
答案 0 :(得分:242)
您的比较器不具有传递性。
让A
成为B
的父级,B
成为C
的父级。自A > B
和B > C
起,A > C
就必须如此。但是,如果在A
和C
上调用比较器,则会返回零,即A == C
。这违反了合同,因此抛出了异常。
图书馆检测到这个并让你知道,而不是表现得不正常,这是相当不错的。
满足compareParents()
中传递性要求的一种方法是遍历getParent()
链而不是仅查看直接祖先。
答案 1 :(得分:34)
因为这是我在Google上搜索此错误时得到的,我的问题是我有
if (value < other.value)
return -1;
else if (value >= other.value)
return 1;
else
return 0;
value >= other.value
应该(显然)实际上是value > other.value
,这样你实际上可以用相同的对象返回0。
答案 2 :(得分:22)
违反合同通常意味着比较器在比较对象时没有提供正确或一致的值。例如,您可能希望执行字符串比较并强制将空字符串排序到最后:
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 :(得分:11)
即使你的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;
}
答案 4 :(得分:6)
在我们的案例中得到了这个错误,因为我们不小心翻了s1和s2的比较顺序。所以要小心。这显然比以下更复杂,但这是一个例子:
s1 == s2
return 0;
s2 > s1
return 1;
s1 < s2
return -1;
答案 5 :(得分:3)
我已经看到这发生在一段代码中,其中经常重复检查空值:
if(( A==null ) && ( B==null )
return +1;//WRONG: two null values should return 0!!!
答案 6 :(得分:3)
Java严格意义上不检查一致性,只会在遇到严重问题时通知您。此外,它不会从错误中提供太多信息。
我对我的分拣机中发生的事情感到困惑,并且做了严格的一致性检查,也许这会对你有所帮助:
/**
* @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));
}
}
}
答案 7 :(得分:3)
在我的情况下,我做了类似以下的事情:
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);
我忘记检查的是a.someField和b.someField都为空。
答案 8 :(得分:1)
如果$v
,则期望compareParents(s1, s2) == -1
。对于您的代码,并非总是如此。
特别是compareParents(s2, s1) == 1
。
这只是可能的问题之一。
答案 9 :(得分:1)
就我而言,这是一种无限排序。 也就是说,首先该行根据条件向上移动,然后同一行向下移动到同一位置。 我在最后添加了一个条件,明确确定了行的顺序。
答案 10 :(得分:0)
您无法比较像这样的对象数据:s1.getParent() == s2
- 这将比较对象引用。您应该覆盖Foo类的equals function
,然后将其与s1.getParent().equals(s2)
答案 11 :(得分:0)
编辑虚拟机配置对我有用。
-Djava.util.Arrays.useLegacyMergeSort=true