我使用以下代码按降序对列表进行排序
List<Integer> list=Arrays.asList(Integer.MAX_VALUE, -1);
list.sort((x, y) -> y-x);
System.out.println(list)
结果是
[-1, 2147483647]
现在,我知道我不应该写y-x,因为它会导致溢出问题。
但问题是为什么输出呢?我认为输出为[2147483647, -1]
因为-1 - Integer.MAX_VALUE
是-2147483648
,仍然是负整数,并且操作似乎不受溢出问题的影响。
我做错了什么?
答案 0 :(得分:5)
正如您可以在页面底部附近的Oracle's Object Ordening Java Tutorial中阅读:
您可能想要替换最终的return语句 比较简单:
除非你是绝对的,否则不要这样做 确定没有人会有负面的员工编号! 这个伎俩 通常不起作用,因为有符号整数类型不够大 表示两个任意有符号整数的差异。如果我是 一个大的正整数,j是一个大的负整数,i - j会 溢出并返回负整数。生成的比较器 违反了我们一直在谈论的四项技术限制之一 (传递性)并产生可怕的,微妙的错误。这不是一个 纯粹的理论关注;人们被它焚烧了。return e1.number() - e2.number();
这里描述的情况是OP遇到的情况:两个整数之间的差异超过Integer.MAX_VALUE
,因此在比较期间会溢出,导致意外的排序。
答案 1 :(得分:2)
我刚刚做了@Robin Topper所说的话。
import java.util.*;
public class Test {
public static void main(String... args) {
List<Integer> list = Arrays.asList(Integer.MAX_VALUE, -1);
list.sort((x, y) -> {
System.out.println("x: " + x + " y: " + y);
System.out.println("y - x = " + (y - x));
return y - x;
});
System.out.println(list);
}
}
我得到了
x: -1 y: 2147483647
y - x = -2147483648
[-1, 2147483647]
我们可以看到
List#sort(Comparator)以相反的顺序将值。Comparator
应用于
Comparator
,-1
和2147483647
,(y - x)
部分(2147483647 - -1
)溢出为-2147483648
这是否定的,-1
小于2147483647
。这是源代码。
@SuppressWarnings({"unchecked", "rawtypes"})
default void More ...sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
答案 2 :(得分:2)
在我们开始之前,您需要知道比较器根据其设计将对(x,y)视为已排序compare(x,y) < 0
(就像自然(升序)订单一样:x<y
然后x-y<0
)。
整数溢出
在数学世界中,如果对(x,y)中的x和y按升序排列,我们可以将其写为x<y
,也可以将其重写为x-y<0
。降序可以表示为x>y
,可以重写为x-y>0
但这种重写只有在数学世界中才有可能。在IT世界中,int
等数字类型具有最小值和最大值,如果我们尝试计算超出该范围的值,我们将面临integer overflow。你的例子就是其中一个例子。如果x = -1
和y = 2147483647
,则比较计算y-x
将返回-2147483648
y - x
(2147483647) - (-1) = -2147483648
而不是正2147483648
。
因此,返回 不正确的 (和否定)结果,使排序算法“认为”x
和y
的值是正确的顺序。
等等,如果结果是错误的,那么我们如何获得升序顺序[-1, 2147483647]
中的结果元素?这个“不正确”的订单不应该下降吗?
不,因为比较器返回y-x
描述了降序顺序。看一看。 x
时,y
中使用的元素Comparator.compare(x,y)
和Comparator.compare(x,y)<0
被视为处于正确的顺序。如果我们使用比较器的代码,我们将获得y-x<0
,顺序为y<x
(或更简单x>y
)。
如果我们更改列表中的一些小元素
,也可以很容易地观察到List<Integer> list = Arrays.asList(-1, 2, 5);
在排序后我们将获得
[5, 2, -1]
简而言之:
-1
和2147483647
的顺序”,以便它们保持在原位。