最近一直在做底层方面的研究,所以这段时间就没写java相关的东西,但恰巧今天同事问我一个问题,在帮他解决完这个问题之后,我发现,这个问题对java新手来说还是非常容易犯的,所以在这里记录下。

首先看下面这段代码:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class Test {

  public static void main(String[] args) {
    List<Long> l = new ArrayList<>();
    for (int i = 0; i < 100000; i++) {
      l.add(ThreadLocalRandom.current().nextLong());
    }
    l.sort((o1, o2) -> (int) (o1 - o2));
    // l.sort(Long::compare);
  }
}

这段代码的功能就是对list进行排序,list内元素类型是long。

一眼看上去好像没啥大问题,执行看下:

Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
  at java.base/java.util.TimSort.mergeLo(TimSort.java:781)
  at java.base/java.util.TimSort.mergeAt(TimSort.java:518)
  at java.base/java.util.TimSort.mergeCollapse(TimSort.java:448)
  at java.base/java.util.TimSort.sort(TimSort.java:245)
  at java.base/java.util.Arrays.sort(Arrays.java:1516)
  at java.base/java.util.ArrayList.sort(ArrayList.java:1749)
  at io.ytcode.game.test/test.Test.main(Test.java:14)

额,报错了(可能需要多执行几次才会报错,但并不影响本文内容),为什么呢?

这段代码大部分逻辑用的都是官方的api,所以这些地方肯定是没问题的,需要我们自己写逻辑的唯一的地方就是list.sort方法传递的参数:Comparator。

看下我们怎么写的,我们返回了 (int) (o1 - o2),看出问题了吗?

o1 - o2的结果还是long啊,如果这个值大于int范围,在我们把它转成int后,结果就溢出处理了,这时,该表达式返回的结果和我们预期的结果就不相同了。

我估计很多人都踩过这坑吧。

那正确的解决方式是什么呢?

把上面程序中的sort行注释掉,用它下面Long::compare的sort行,再试试是不是就可以了。

看下Long::compare的对应实现:

// java.lang.Long
public static int compare(long x, long y) {
    return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

这才是long的compare的标准方式!

还是那句话,官方库带有的方法就用官方的,这能让你少踩很多坑。

完。


更多原创文章,请关注我微信公众号:

底层技术研究


wangyuntao
30 声望3 粉丝