Java - 列出<整数>排序,比较器和溢出

时间:2017-07-18 12:52:21

标签: java overflow comparator

我使用以下代码按降序对列表进行排序

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,仍然是负整数,并且操作似乎不受溢出问题的影响。 我做错了什么?

3 个答案:

答案 0 :(得分:5)

正如您可以在页面底部附近的Oracle's Object Ordening Java Tutorial中阅读:

  

您可能想要替换最终的return语句   比较简单:

return e1.number() - e2.number(); 
     除非你是绝对的,否则不要这样做   确定没有人会有负面的员工编号! 这个伎俩   通常不起作用,因为有符号整数类型不够大   表示两个任意有符号整数的差异。如果我是   一个大的正整数,j是一个大的负整数,i - j会   溢出并返回负整数。生成的比较器   违反了我们一直在谈论的四项技术限制之一   (传递性)并产生可怕的,微妙的错误。这不是一个   纯粹的理论关注;人们被它焚烧了。

这里描述的情况是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应用于

  1. 使用给定的Comparator
  2. 适用于-12147483647
  3. (y - x)部分(2147483647 - -1)溢出为-2147483648这是否定的,
  4. 确实-1小于2147483647
  5. 这是源代码。

    List#sort(Comparator)

    @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);
        }
    }
    

    Arrays#sort(Comparator)

    public static void sort(int[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }
    

    DualPovotQuicksort#sort()

答案 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 = -1y = 2147483647,则比较计算y-x将返回-2147483648

     y       -   x
(2147483647) - (-1) = -2147483648 

而不是正2147483648

因此,返回 不正确的 (和否定)结果,使排序算法“认为”xy的值是正确的顺序。

等等,如果结果是错误的,那么我们如何获得升序顺序[-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]

简而言之:

  • 排序算法使用描述降序顺序
  • 的Comparator
  • 但是由于比较器出错(由整数溢出引起),因此它不会通知排序算法需要“更改-12147483647的顺序”,以便它们保持在原位。