无用知识点系列一:new ArrayList<>() 与 new ArrayList<>(0)有什么区别。
ArrayList中的两个空数组
1 | private static final Object[] EMPTY_ELEMENTDATA = {}; |
在ArrayList的源码中,分别有两个空数组对象 EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,他们的区别是在于有参构造使用 EMPTY_ELEMENTDATA 而无参构造则使用 DEFAULTCAPACITY_EMPTY_ELEMENTDATA。在现在,你可能会觉得这玩的什么蛇皮操作,为什么不统一改成 EMPTY_ELEMENTDATA 或者 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 呢,这样操作肯定是有它的道理,我们继续往下看。
空数组作用解析
1 | transient Object[] elementData; // list内部存储的数据 |
首先我们看看扩容的逻辑: ==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 个小时,坚持一年就浪费了半天时间,离废物又能更近一步了。