a < b 与 a - b < 0
在查看集合源码时, 发现许多地方都用到了
a - b < 0, 并注释了overfolw-conscious code如:// overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity);于是想探讨一下它们的区别。
分析
如果 a, b 都没有发生溢出的情况下 \(a - b < 0\) 和 \(a < b\) 显然是一样的, 所以要当它俩至少有一个溢出时这个问题才有意义, 以下分三种情况讨论 (为简化情况, 假设 a, b 都大于 0 )。
ps: 在计算机中的正数溢出之后, 一般变为一个绝对值较大的负数。
a 溢出 b 不溢出
a 溢出, 导致 a 变为负数; b 没溢出, 所以还是正数。
事实: 此时是 \(b < Integer.MAX\_VALUE < a\)
- 
使用 a < b 时: 这个表达式为 true, 但与事实 \(b < a\) 不符。
 - 
使用 a - b < 0 时:
- b 很小时: \(a - b\) 不再溢出, 但 \(a - b < 0\), 与事实 \(b < a\) 不符。
 - b 很大时, \(a - b\) 再次溢出, 导致 \(a - b > 0\), 符合预期。
 
 
a 不溢出 b 溢出
b 溢出, 导致 b 变为负数; a 没溢出, 所以还是整数。
事实: 此时是 \(a < Integer.MAX\_VALUE < b\)。
- 
使用 a < b 时: 这个表达式为 \(false\), 但与事实 \(a < b\) 不符。
 - 
使用 a - b < 0 时:
- a 很小时: \(a - b\) 不再溢出, 但 \(a - b > 0\), 与事实 \(a < b\) 不符。
 - a 很大时: \(a - b\) 再次溢出, 导致 \(a - b < 0\) , 符合预期。
 
 
a, b都溢出
无法判断
以上的分析中用了"很小", "很大"的字眼, 其实是有限定的, 例如:
int a = Integer.MAX_VALUE;
a += 20;  // a 若溢出 20, 则 20 为临界点
int b = 15;  // 此时 b "很小"
int c = 25;  // 此时 c "很大"
结论
综合上面的分析,两种写法都不是万能的,在某些情况下还是与预期不符,但 JDK之所以采用 \(a - b < 0\) 的写法是因为: 在确保 a 和 b 数值接近的情况下,其中一个先溢出,另一个还没溢出但是也很大了,此时 \(a - b < 0\) 的写法刚好得到正确结果。
JDK 采用这种写法,一般是在各种空间的分配中,例如 ensureCapacity()。而分配空间时,新空间大小虽然比旧空间大,但它俩往往是差不多的(也即刚说的 "数值接近"),使用这种写法可以巧妙的化解溢出的问题。

                
            
        
浙公网安备 33010602011771号