“比较方法违反了其总合同!”

时间:2017-09-24 03:45:25

标签: java

我搜索了一些同样的问题而且我知道原因。但是只发生一次坠机,所以我来这里问你可能会导致什么事情崩溃。

    Collections.sort(downloadTasks, new Comparator<DownloadTask>() {
        @Override
        public int compare(DownloadTask lhs, DownloadTask rhs) {
            if (lhs == null || rhs == null) return 0;
            return (int) (lhs.mTaskInfo.time - rhs.mTaskInfo.time);
        }
    });

错误是:

  

java.lang.IllegalArgumentException:比较方法违反了其一般合同!在java.util.TimSort.mergeHi(TimSort.java:864)     在java.util.TimSort.mergeAt(TimSort.java:481)     在java.util.TimSort.mergeCollapse(TimSort.java:406)     在java.util.TimSort.sort(TimSort.java:210)     在java.util.TimSort.sort(TimSort.java:169)     在java.util.Arrays.sort(Arrays.java:2010)     在java.util.Collections.sort(Collections.java:1883)

如您所见,我通过time成员比较两个对象。 timelong类型。

我认为崩溃可能来自:

  • 将long转换为int是否导致崩溃?
  • if(lhs == null || rhs == null)返回0; 但理论上lhs和rhs都不为null。

修改

如果lhsrhs可以为空,我该怎么办?

EDIT For Android

在Android中,Long.compare()需要API 19.您可以在API 19下执行此操作:

public static int compare(long lhs, long rhs) {
    return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1);
}

2 个答案:

答案 0 :(得分:5)

这两种情况都有问题。

  

if (lhs == null || rhs == null) return 0;

如果你有[123, null, 234],那么你将123等同于null,将null等同于234,并通过及物性来获得123 {1}}等于234。但这不是比较器返回的原因。

此处的解决方案是禁止null或将所有nulls排序到底部(或顶部),即只返回0 两者null,否则返回1-1(取决于左侧或右侧null)。

  

return(int)(lhs.mTaskInfo.time - rhs.mTaskInfo.time);

考虑将Integer.MAX_VALUE + 10进行比较。两者之间的差异是Integer.MAX_VALUE + 1。将其转换为int换行到Integer.MIN_VALUE。然后,相反的比较应该为您提供- Integer.MIN_VALUE,但that is Integer.MIN_VALUE again due to overflow

此处的解决方案是使用Long.compare(a,b)

答案 1 :(得分:2)

比较方法的“一般合同”在compare(T o1, T o2)方法的javadoc中有详细记录:

  

实施者必须确保所有sgn(compare(x, y)) == -sgn(compare(y, x))xy。 (这意味着compare(x, y)当且仅当compare(y, x)抛出异常时才必须抛出异常。)

     

实现者还必须确保关系具有传递性:((compare(x, y)>0) && (compare(y, z)>0))隐含compare(x, z)>0

     

最后,实施者必须确保compare(x, y)==0暗示所有z都sgn(compare(x, z))==sgn(compare(y, z))

那么,你违反了这三条规则中的哪一条? 全部3

违反了第一条规则,因为timelong,因此(int) (lhs.mTaskInfo.time - rhs.mTaskInfo.time)可以等于Integer.MIN_VALUE。如果您将两个参数翻转为compare(),结果仍为Integer.MIN_VALUE,因为int值无法存储否定值。

违反了第二条规则,因为int溢出。由于timelong,我会假设它们包含标准的毫秒值,而int只能存储最多 24天的值。所以我们说输入是Jan 1, Jan 15, Jan 30compare(Jan 1, Jan 15)为+14天,compare(Jan 15, Jan 30)为+15天,但compare(Jan 1, Jan 30)应为+29天,但溢出的回报类似于-19天。糟糕!!!!

由于错误的空检查,违反了第三条规则。假设输入为null, Jan 1, Jan 2compare(null, Jan 1)为0,compare(null, Jan 2)也是compare(Jan 1, Jan 2),但int为+1天。

解决方案

使用Long.compare(long x, long y)可以轻松修复MIN_VALUE溢出和null问题。

对于public int compare(DownloadTask lhs, DownloadTask rhs) { if (lhs == null) { if (rhs == null) return 0; return -1; } if (rhs == null) return 1; return Long.compare(lhs.mTaskInfo.time, rhs.mTaskInfo.time); } 问题,您需要确定空值是排序还是最后排序。以下将首先对空值进行排序:

public int compare(DownloadTask lhs, DownloadTask rhs) {
    return (lhs == null ? (rhs == null ? 0 : -1)
            : (rhs == null ? 1 : Long.compare(lhs.mTaskInfo.time, rhs.mTaskInfo.time)));
}

或单语句版本:

 override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if indexPath.item >= allEvents.count - 1 {
            // print("paginating for post")
            paginationHelper.paginate(completion: { [unowned self] (events) in
                self.allEvents.append(contentsOf: events)

                DispatchQueue.main.async {
                    self.collectionView?.reloadData()
                }
            })
        }else{
            print("Not paginating")
        }
    }