“比较法违反了它的总合同!”
有人可以用简单的术语来解释我,为什么这个代码抛出一个exception,“比较方法违反了它的一般合同!”,我该如何解决?
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
这违反了合同,因此抛出exception。
图书馆很好地检测这个,让你知道,而不是行为不正常。
在compareParents()
满足传递性要求的一种方法是遍历getParent()
链,而不是只查看直接祖先。
正因为这是我在Google上search这个错误时所得到的,所以我的问题是我有这个问题
if (value < other.value) return -1; else if (value >= other.value) return 1; else return 0;
value >= other.value
应该(显然)实际上是value > other.value
以便您可以实际返回0与相同的对象。
违反合同通常意味着比较对象在比较对象时没有提供正确或一致的值。 例如,您可能需要执行string比较,并强制使用空string进行sorting,结果如下:
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 );
在我们的情况下,得到这个错误是因为我们不小心翻转了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。 而事实certificate,由于浮点运算的微小错误,导致传递属性破坏,他们不应该的四舍五入错误! 所以我重写了代码来考虑非常小的差异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; }
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)); } } }
我已经看到这发生在一个经常重复检查空值的代码块中:
if(( A==null ) && ( B==null ) return +1;//WRONG: two null values should return 0!!!
你不能比较像这样的对象数据: s1.getParent() == s2
– 这将比较对象引用。 您应该重写Foo类的equals function
,然后像这样比较它们s1.getParent().equals(s2)
就我而言,我正在做如下的事情:
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都为空。