目录

原文地址:java - Difference between if (a - b < 0) and if (a < b) - Stack Overflow

问题

在阅读 Java 的 ArrayList 源代码时,我注意到在 if 语句中的一些比较语句。

在 Java 7 之中,grow(int) 方法使用了如下代码:

if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;

在 Java 6 中,没有 grow 方法。但方法 ensureCapacity(int) 却使用如下方式比较数值:

if (newCapacity < minCapacity)
    newCapacity = minCapacity;

变更比较语句写法的原因是什么呢?为了性能还是说只是代码风格的变化?

我可以理解,与 0 作比较速度更快,但同时却需多执行一个减法,这不是额外的开销吗?同时对于编译后的字节码来说,做差比较需要涉及两个指令(ISUBIF_ICMPGE),而直接比较只需要涉及 IFGE 一条指令。

赞同最多的回答

a < ba - b < 0 可以表示不同的含义。

考虑如下场景:

int a = Integer.MAX_VALUE;
int b = Integer.MIN_VALUE;
if (a < b) {
    System.out.println("a < b");
}
if (a - b < 0) {
    System.out.println("a - b < 0");
}

执行结果是:只会打印 a - b < 0。因为 a < b 显然是 false,但 a - b 产生了溢出,结果是 -1,因此结果为 true。

  • 译者注:
    int max = Integer.MAX_VALUE; //  2147483647
    int min = Integer.MIN_VALUE; // -2147483648
    

说回问题本身,我们假设数组长度本身已经非常接近于 Integer.MAX_VALUE 来看一看。ArrayList 之中的代码如下:

// 译者注:可以查看问题中的第一个链接 grow(int) 方法
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);

oldCapacity 非常接近 Integer.MAX_VALUE,因此 newCapacity(即oldCapacity + 0.5 * oldCapacity)有可能产生溢出,假设溢出后值为 Integer.MIN_VALUE (负值)。然后,减去 minCapacity下溢变为正值(译者注:假设参数minCapacity为正值)。

因此第一个 if 块中的代码将不会被执行。但假设代码条件写作 if (newCapacity < minCapacity),结果将会是 true(因为 newCapacity 是负值),这将导致 newCapacity 被强行指定为 minCapacity 而没有考虑 oldCapacity

这个溢出问题将会被下一个 if 语句所处理。当 newCapacity 溢出后,此 if 的结果为 trueMAX_ARRAY_SIZEInteger.MAX_VALUE - 8,所以 Integer.MIN_VALUE - (Integer.MAX_VALUE - 8) > 0true。因此 newCapacity 可以被正确的处理:hugeCapacity 方法将返回 MAX_ARRAY_SIZEInteger.MAX_VALUE

注意:这就是对应 Java 代码中注释 // overflow-conscious 的含义。