无用知识点系列一:new ArrayList<>() 与 new ArrayList<>(0)有什么区别。
ArrayList中的两个空数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private static final Object[] EMPTY_ELEMENTDATA = {};private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};public ArrayList (int initialCapacity) { if (initialCapacity > 0 ) { this .elementData = new Object[initialCapacity]; } else if (initialCapacity == 0 ) { this .elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); } } public ArrayList () { this .elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
在ArrayList的源码中,分别有两个空数组对象 EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,他们的区别是在于有参构造使用 EMPTY_ELEMENTDATA 而无参构造则使用 DEFAULTCAPACITY_EMPTY_ELEMENTDATA。在现在,你可能会觉得这玩的什么蛇皮操作,为什么不统一改成 EMPTY_ELEMENTDATA 或者 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 呢,这样操作肯定是有它的道理,我们继续往下看。
空数组作用解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 transient Object[] elementData; private static final int DEFAULT_CAPACITY = 10 ; public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; } private void ensureCapacityInternal (int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity (Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity (int minCapacity) { modCount++; if (minCapacity - elementData.length > 0 ) grow(minCapacity); } private void grow (int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1 ); if (newCapacity - minCapacity < 0 ) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0 ) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
首先我们看看扩容的逻辑: ==oldCapacity + (oldCapacity >> 1)== 。说明容器扩容并非 1.5 倍,因为没有小数位,所以当尾数不为 0 的话,就相当于舍去了 0.5,所以扩容为 1.5 倍舍去余数。至于为什么我感觉大家二进制都会,就不多叭叭了。
接下来是 add 方法,add 的时候,elementData 等于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的时候,是会直接给数组扩容成 10 的大小,如果是 EMPTY_ELEMENTDATA 则不做处理,直接扩容成了 1。
由此可得:
如果是无参构造的话,容器的容量为:0、10、15、22、33 … 如果是有参构造且参数为 0,容器的容量为:0、1、2、3、4、6、9、13 … 我们会发现无参构造容器的大小只需要一次扩容就能到 10,而有参构造且参数为 0 的时候,需要七次才能突破 10 的大关。为什么作者这么设计呢?我觉得可能是作者考虑到了有这么一种场景:集合包含的元素不确定,但基本数量都会小于 10,这时使用有参构造且参数为 0 则可以大量的节省内存。
假设我们有个百万级别并发访问的api,返回的数组都是两三个,这时候用有参为 0 的构造方法,则可以节省 (10 - 2) * 10 000 个数组空间,四舍五入相当于一个亿。
好吧,我真不知道这个设计有啥用,我猜想也没有几个人会用这种有参构造方法吧。
作者的话 今天的每天一个没用知识点结束,是不是学完感觉到浪费了两分钟时间,每天一个知识点,一年就是 365 * 2 / 60 = 12 个小时,坚持一年就浪费了半天时间,离废物又能更近一步了。