《奇怪为什么ArrayList初始化容量大小为10?.docx》由会员分享,可在线阅读,更多相关《奇怪为什么ArrayList初始化容量大小为10?.docx(6页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、背景看ArrayList源码时,无意中看到ArrayList的初始化容量大小为10,这就奇怪了! 我们都知道ArrayList和HashM叩底层都是基于数组的,但为什么ArrayList不 像用HashMap那样用16作为初始容量大小,而是采用10呢?于是各方查找资料,求证了这个问题,这篇文章就给大家讲讲。为什么HashMap的初始化容量为16?在聊ArrayList的初始化容量时,要先来回顾一下HashMap的初始化容量。这里 以Java 8源码为例,HashMap中的相关因素有两个:初始化容量及装载因子:/为为* The default initial capacity - MUST be
2、 a power of two./static Mt DEFAULT_INEAL_CAPACITY = 1 4; / aka 16/兴兴* The load factor used wke八 八。八e specified in coistriACtor./static filial float DEFAULTLLOAD_FA6TOR = 0.75在HashM叩当中,数组的默认初始化容量为16,当数据填充到默认容量的0.75 时,就会进行2倍扩容。当然,使用者也可以在初始化时传入指定大小。但需要 注意的是,最好是2的n次方的数值,如果未设置为2的n次方,HashMap也 会将其转化,反而多了一步
3、操作。关于HashM叩的实现原理的内容,这里就不再赘述,网络上已经有太多文章讲 这个了。有一点我们需要知道的是HashM叩计算Key值坐标的算法,也就是通 过对Key值进行Hash,进而映射到数组中的坐标。此时,保证HashM叩的容量是2的n次方,那么在hash运算时就可以采用位 运行直接对内存进行操作,无需转换成十进制,效率会更高。通常,可以认为,HashM叩之所以采用2的n次方,同时默认值为16,有以下 方面的考量: 减少hash碰撞; 提高M叩查询效率; 分配过小防止频繁扩容; 分配过大浪费资源;总之,HashM叩之所以采用16作为默认值,是为了减少hash碰撞,同时提升 效率。Arra
4、yList的初始化容量是10吗?下面,先来确认一下ArrayList的初始化容量是不是10,然后在讨论为什么是这 个值。先来看看Java 8中,ArrayList初始化容量的源码:/次为* Default initial capacity./private static filial Mt DEFAUL匚CAPACITY =工。;很明显,默认的容器初始化值为10。而且从JDK1.2到JDK1.6,这个值也始终都 为10。从JDK1.7开始,在初始化ArrayList的时候,默认值初始化为空数组:* Scared CMptg array ihstance used for default siz
5、ed CMptg instances. Wedisti八guish this froEMPTY_ELEMENTDATA to khow how ck to inflate when * first elekvxeiat is added.Vprivate static filial Object DEFAULTCAPACITY_EMPTY_ELEM ENTDATA = ;/米兴* Constructs av CMptg list with a八 initial capacity of ten.Vpublic ArrayList() t.eleieiatData = DEFAULTCAPACIT
6、Y_EMPTY_ELEMENTDATA;)此处肯定有朋友说,Java 8中ArrayList默认初始化大小为0,不是10。而且还会发现构造方法上的注释有一些奇怪:构造一个初始容量10的空列表。什么鬼? 明明是空的啊!保存疑问,先来看一下ArrayList的add方法:public boolean add(E c) 。八snYeCapacitgl八tCHna(size + 1); / /八crenAC八ts kodCo八七e/ei/we 八 tDa sizc+J = e;return true;在add方法中调用了 ensureCapacitylnternal方法,进入该方法一开始是一个空容器所
7、以size=O传入的wxhCapacitg二工:private void eiAreCapacitylitemal(iiat iiCapacity eiAreExplicitCapacitcalciAlateCapacity(eleieitData, iiincapacity);上述方法中先通过calculateCapacity来计算容量:private static iit calc(AlateCapacity(Object elekvxetData, int Mi八Capacitg) if (eleeata = DEFAULTCAPACITY_EMPTY_ELEMENTDATA) retu
8、rn MatKiax(DEFAULT_CA.PAClTYJ i O) grow(Mi 八 Cqpac/tg);)private void grow(iit mSCapacitg) / overflow -co八scions codeiit oldCapacity = eleieitlata.length;/kt iewCapacity = oldCapacity + (oldCapacity 1);if (iewCapacity - Mi八Capacity O)MwCapacity = h(AgeCapacitg(Mi 八 Capacitg);/ iiiCapacity is usually
9、close to size, s。this is a win:eleieintData = Arrays.copyOf(eleetData, inewCapacity);上述代码中grow方法是用来处理扩容的,将容量扩容为原来的1.5倍。了解上面的处理流程,我们会发现,本质上ArrayList的初始化容量还是10,只不过使用懒加载而已,这是Java 8为了节省内存而进行的优化而已。所以,自始至终,ArrayList的初始化容量都是100这里再多提一卜懒加载的好处,当有成千上万的ArrayList存在程序当中,10个 对象的默认大小意味着在创立时为底层数组分配10个指针(40或80字节)并 用空
10、值填充它们,一个空数组(用空值填充)占用大量内存。如果能够延迟初始 化数组,那么就能够节省大量的内存空间。Java 8的改动就是出于上述目的。为什么ArrayList的初始化容量为10?最后,我们来探讨一下为什么ArrayList的初始化容量为10。其实,可以说没有 为什么,就是感觉10挺好的,不大不小,刚刚好,眼缘!首先,在讨论HashM叩的时候,我们说到HashMap之所以选择2的n次方, 更多的是考虑到hash算法的性能与碰撞等问题。这个问题对于ArrayList的来说 并不存在。ArrayList只是一个简单的增长阵列,不用考虑算法层面的优化。只要 超过一定的值,进行增长即可。所以,理
11、论上来讲ArrayList的容量是任何正值 即可。ArrayList的文档中并没有说明为什么选择10,但很大的可能是出于性能损失与 空间损失之间的最正确匹配考量。10,不是很大,也不是很小,不会浪费太多的内 存空间,也不会折损太多性能。如果非要问当初到底为什么选择10,可能只有问问这段代码的作者Josh Bloch 了吧。如果你仔细观察,还会发现一些其他有意思的初始化容量数字:ArrayList与Vector初始化容量一样,为10; HashSet、HashMap初始化容量一 样,为16;而HashTable独独使用11,又是一个很有意思的问题。小结有很多问题是没有明确原因、明确的答案的。就好像一个女孩儿对你没感觉,可 能是因为你不够好,也可能是她已经爱上别人了,但也有很大可能你是不会知道 答案。但在寻找原因和答案的过程中,还是能够学到很多,成长很多的。没有对 比就没有伤害,比方HashMap与ArrayList的比照,没有比照就不知道是否适合, 还比方HashMap与ArrayList。当然,你还可以试试特立独行的HashTable,或 许适合你呢。