数学问题:newCapacity
< minCapacity
和 newCapacity - minCapacity < 0
代表相同的含义吗?答案:是,在计算机中不同,因为数字用的是有限位的补码,也正是因此才会有考虑溢出的代码。
private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);}
补码在表示有符号数的时候,最高位用来当做符号位,0代表正数,1代表负数。
private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}
最大值整数加1就会变成最小值整数。其实,将int的这些数字看起来很像是一个圆环,从0开始,逆时针增大,到最大值的时候,再加1就变为最小值,然后再逆时针增大到0。
具体如下:
在jdk源码中,会有很多考虑了溢出而编写的代码,这些代码前会有注释:"overflow-conscious code",说明下面这段代码是考虑了溢出的情况的。最经典的代码就是里ArrayList的grow方法
代码如下(ArrayList.grow):
package com.atcm.until;public class TestArrayList {/*** Default initial capacity.*/private static final int DEFAULT_CAPACITY = 10;private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// transient Object[] elementData; // non-private to simplify nested class accessint length = 0; // elementData的长度int capacity = 0; // elementData的容量Object obj = null;/*** The size of the ArrayList (the number of elements it contains).** @serial*/private int size;public TestArrayList(){int length = 0; // elementData的长度int capacity = 0; // elementData的容量}public static void main(String[] args) {TestArrayList arrayList = new TestArrayList();for (int i = 0; i < 100; i++) {arrayList.add("数据");}}public boolean add(Object e) {ensureCapacityInternal(size + 1); // Increments modCount!!//elementData[size++] = e;size++;obj = e;return true;}private void ensureCapacityInternal(int minCapacity) {//if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//}minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);ensureExplicitCapacity(minCapacity);}private void ensureExplicitCapacity(int minCapacity) {// overflow-conscious codeif (minCapacity - this.length > 0) {System.out.println("====> 开始扩容:");grow(minCapacity);println();}}/*** Increases the capacity to ensure that it can hold at least the* number of elements specified by the minimum capacity argument.** @param minCapacity the desired minimum capacity*/private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = this.length;int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win://elementData = Arrays.copyOf(elementData, newCapacity);capacity = newCapacity; // 相当新数组length = capacity; // 相当新数组替换旧数组}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}public void println() {if (size == 0) {System.out.println("扩容结果:size= " + size + " length=" + length + " capacity=" + capacity);}else {System.out.println("扩容结果:size= " + size + " length=" + length + " capacity=" + capacity + " capacity/size=" + 1.0 * capacity / size);}}
}
这段模拟代码的功能是对ArrayList的存储进行扩容,扩大为原来的1.5倍。那么在计算扩展后的容量时就有可能会溢出。
结果:
====> 开始扩容:
扩容结果:size= 0 length=10 capacity=10
====> 开始扩容:
扩容结果:size= 10 length=15 capacity=15 capacity/size=1.5
====> 开始扩容:
扩容结果:size= 15 length=22 capacity=22 capacity/size=1.4666666666666666
====> 开始扩容:
扩容结果:size= 22 length=33 capacity=33 capacity/size=1.5
====> 开始扩容:
扩容结果:size= 33 length=49 capacity=49 capacity/size=1.4848484848484849
====> 开始扩容:
扩容结果:size= 49 length=73 capacity=73 capacity/size=1.489795918367347
====> 开始扩容:
扩容结果:size= 73 length=109 capacity=109 capacity/size=1.4931506849315068Process finished with exit code 0