目录
一、1. 追入:DEFAULTCAPACITY_EMPTY_ELEMENTDATA
二、1. 追入:ensureCapacityInternal
二、1.1. 追入:ensureExplicitCapacity
三、1. 追入:ensureCapacityInternal
三、1.1. 追入:ensureExplicitCapacity
四、1. 追入:ensureCapacityInternal
四、1.1. 追入:ensureExplicitCapacity
ArrayList扩容原理
1.ArrayList底层数据结构是数组!!!
2.数组的特点:固定长度,顺序存储,有下标,可重复。
代码
源码
一、追进ArrayList源码
得到:
一、1. 追入:DEFAULTCAPACITY_EMPTY_ELEMENTDATA
得到:-- 意思是:创建一个final修饰的Object类型的空的常量数组。
总结,将空数组赋值给elementData这个属性。
此时elementData数组是空的。
DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组也是空的。
二、第一次追进add源码
得到:
(E e):就是你传入的 "aa" 。
size:现在等于0。
二、1. 追入: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。
继续执行下一条语句:
二、1.1. 追入:ensureExplicitCapacity
此时 minCapacity = 10。
第一条语句:modCount++;追进去看看:
发现 modCount = 0。
然后判断 minCapacity - elementData.length 是否大于 0 。因为前面说过elementData数组是空的,所以 10 - 0 是大于 0 的。
条件成立,执行grow(minCapacity);
二、1.1.1. 追入: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;
前面说过 size = 0;e = "aa";
所以:elementData[0] = "aa";
然后size++
此时 size = 1。
第一遍追进之后数据的改变:
size = 1。
elementData 数组长度为 10。
newCapacity = 10。
modCount = 1。
三、第二次追进add源码
得到:
此时 size = 1。
三、1. 追入:ensureCapacityInternal
此时 minCapacity = size + 1。
minCapacity = 2。
判断 elementData 和 DEFAULTCAPACITY_EMPTY _ELEMENTDATA是否相等,因为经过第一次赋值导致 elementData 已经是 10了。所以条件不成立,不执行里面的语句。
继续执行下一条语句。
三、1.1. 追入:ensureExplicitCapacity
得到:
此时 modCount = 1。
判断 minCapacity - elementData.length 是否大于 0 。
因为此时elementData数组长度为 10。所以条件不成立,不执行里面语句。
所以此时返回到 三、第二次追进add源码 执行下一条语句。
elementData[size++] = e;
前面说过 size = 1;e = "bb";
所以:elementData[1] = "bb";
然后size++
此时 size = 2。
第二遍追进之后数据的改变:
size = 2。
elementData 数组长度为 10。
newCapacity = 10。
modCount = 2。
第十遍追进之后数据的改变:
size = 10。
elementData 数组长度为 10。
newCapacity = 10。
modCount = 10。
四、第十一次追进add源码
得到:
此时 size = 10 。
四、1. 追入:ensureCapacityInternal
此时 minCapacity = size + 1 。
minCapacity = 11 。
判断 elementData 和 DEFAULTCAPACITY_EMPTY _ELEMENTDATA是否相等,因为经过前面赋值导致 elementData 已经是 10了。所以条件不成立,不执行里面的语句。
继续执行下一条语句。
四、1.1. 追入:ensureExplicitCapacity
此时 modCount = 10 。
判断 minCapacity - elementData.length 是否大于 0 。
因为 minCapacity = 11 。elementData 数组长度为 10 。所以条件成立,执行里面的语句。
四、1.1.1. 追入: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 查询慢,增删快。
原因:不是顺序存储,每个结点相连,一个结点中可以存储下一个和上一个结点,这样的话,增删元素,只对相邻的结点有影响,其他结点不受影响;由于没有下标,所以,查询元素时,需要(从头结点或尾结点)遍历。