java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > JavaSE ArrayList扩容原理

JavaSE之ArrayList扩容原理分析

作者:阳光洒落大地

这篇文章主要介绍了JavaSE之ArrayList扩容原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

ArrayList扩容原理

ArrayList底层数据结构是数组!!!

数组的特点:固定长度,顺序存储,有下标,可重复。

代码

源码

追进ArrayList源码

得到:

追入:DEFAULTCAPACITY_EMPTY_ELEMENTDATA

得到:-- 意思是:创建一个final修饰的Object类型的空的常量数组。

总结:

将空数组赋值给elementData这个属性。此时elementData数组是空的。DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组也是空的。

第一次追进add源码

得到:

追入:ensureCapacityInternal

得到:

minCapacity:就等于 (size + 1)。--现在等于1。

然后利用 if判断,前面提到过,elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值得到的,所以现在它们两个相等。

条件成立,就进入,Math.max(DEFAULT_CAPACITY, minCapacity)意思是取括号里的较大的值,咱们现在知道minCapacity的值是1,所以现在追进DEFAULT_CAPACITY的源码去看看:

DEFAULT_CAPACITY的值是10。

所以这行代码的意思是:

minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

将10重新赋值给minCapacity。

此时 minCapacity:等于10。

继续执行下一条语句:

追入:ensureExplicitCapacity

此时 minCapacity =10。

第一条语句:modCount++;追进去看看:

发现 modCount =0。

然后判断minCapacity - elementData.length是否大于 0 。因为前面说过elementData数组是空的,所以 10 - 0是大于 0的。

条件成立,执行grow(minCapacity);

 追入:grow

注意:重点来了

此时 minCapacity = 10。

语句:int oldCapacity = elementData.length;

此时 oldCapacity = 0。

语句:int newCapacity = oldCapacity + (oldCapacity >> 1);

位运算符:>>意思是 / 2。 <<的意思是 * 2。

此时newCapacity = 0。

语句:if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

判断 0 - 10是否小于 0 。

条件成立,执行:newCapacity = minCapacity;

此时newCapacity = 10。

语句:if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

判断 10 -MAX_ARRAY_SIZE是否大于 0。

MAX_ARRAY_SIZE = 2147483639。

条件不成立,不执行:newCapacity = hugeCapacity(minCapacity);

继续往下走。

语句:elementData = Arrays.copyOf(elementData, newCapacity);

新数组 = Arrays.copyOf(旧的数组,新数组的长度):意思是复制数组,将旧数组复制到新的数组。

此时elementData数组长度为 10。

然后返回到二、第一次追进add源码执行下一条语句

elementData[size++] = e;

第一遍追进之后数据的改变

第二次追进add源码

得到:

此时size = 1。

追入:ensureCapacityInternal

此时minCapacity =size + 1。

minCapacity = 2。

判断 elementData 和DEFAULTCAPACITY_EMPTY_ELEMENTDATA是否相等,因为经过第一次赋值导致 elementData已经是 10了。所以条件不成立,不执行里面的语句。

继续执行下一条语句。

追入:ensureExplicitCapacity

得到:

此时modCount = 1。

判断 minCapacity -elementData.length是否大于 0 。

因为此时elementData数组长度为 10。所以条件不成立,不执行里面语句。

所以此时返回到三、第二次追进add源码执行下一条语句。

elementData[size++] = e;

前面说过size = 1;e = "bb";

第二遍追进之后数据的改变

第十遍追进之后数据的改变

size = 10。

elementData数组长度为 10。

newCapacity = 10。

modCount = 10。

第十一次追进add源码

得到:

此时size = 10 。

追入:ensureCapacityInternal

追入:ensureExplicitCapacity

追入:grow

重点:

此时minCapacity = 11 。elementData.length = 10 。

语句:int oldCapacity = elementData.length;

此时oldCapacity = 10 。

语句:int newCapacity = oldCapacity + (oldCapacity >> 1);

意思是:newCapacity = 10 + (10除以 2)

此时newCapacity = 15 。

语句:if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

判断 15 - 11是否小于 0 。

不小于,所以不执行里面的语句:newCapacity = minCapacity;

语句:if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

判断 15-MAX_ARRAY_SIZE是否大于 0。

MAX_ARRAY_SIZE = 2147483639。

条件不成立,不执行:newCapacity = hugeCapacity(minCapacity);

继续往下走。

语句:elementData = Arrays.copyOf(elementData, newCapacity);

意思是:新数组 =Arrays.copyOf(老数组,新数组长度);

elementData = Arrays.copyOf(elementData,15);

所以这是后 elementData 数组长度为 15 。

然后返回到四、第十一次追进add源码执行下一条语句

elementData[size++] = e;

所以:elementData[10] = "第十一次";

然后size++

此时 size = 11。

总结

1、底层创建了一个 Object[]的数组。数组名:elementData。此数组中没有元素。

2、通过List.add 调用 grow()扩容方法,数组长度变为10。

3、在数组存满之前 List.add中不会再调用grow()扩容方法了。

4、当第十一次存入时,List.add再次调用grow()扩容方法。

数组长度会变为原数组长度的1.5倍。

5、扩容不是在老数组基础上拼接的,而是创建了一个1.5倍长度的新数组。

并把老数组的元素复制到新数组。

面试时参考话术

ArrayList底层数据结构是数组,当创建ArrayList对象时,底层初始化了一个空数组,数组是Object类型,数组名是elementData。

当第一次添加元素时,数组长度扩容为10。

……

当第11次添加时,会触发扩容机制,其实就是调用 grow方法,扩容为原数组长度的1.5倍。

每次扩容时,都是创建一个新数组,将老数组的元素通过 Arrays工具类复制到新数组中。elementData 指向了新数组。

ArrayList和 LinkedList 区别?

ArrayList 底层数据结构 数组。

LinkedList 底层数据结构 链表。

功能上区别:

ArrayList 查询快,增删慢。

原因:顺序存储,有索引,可以根据索引,直接定位到元素,所以查询快;由于是顺序存储,新增或者删除,都会对后续的元素有影响。

LinkedList 查询慢,增删快。

原因:不是顺序存储,每个结点相连,一个结点中可以存储下一个和上一个结点,这样的话,增删元素,只对相邻的结点有影响,其他结点不受影响;由于没有下标,所以,查询元素时,需要(从头结点或尾结点)遍历。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文