文章重点总结我在学习中遇到的不易理解的点,以及在面试过程中常遇到的点和题目思路分享.
👉文章默认大家拥有ArrayList和LinkedList的基础知识
基础回归:
⁜ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表
一.分析ArrayList的源代码
1.ArrayList的构造
通过分析ArrayList类下一共有三种构造方法
无参构造方法 | |
---|---|
指定顺序表初始容量 | |
利用其他 Collection 构建 ArrayList,例如自定义comparator比较方法作为实参进行传参 |
2.ArrayList的遍历
迭代器遍历方式介绍:
List<Integer> list = new ArrayList<>();
list.add(1); list.add(2);
list.add(3);
list.add(4);
list.add(5);
Iterator<Integer> it = list.listIterator();
while(it.hasNext()) {
System.out.print(it.next() + " ");
}
迭代器工作示意图:
3.面试高频题:ArrayList的扩容机制
下面将通过分析java底下的源代码进行分析:
简单解释上述编号代码的代码含义:
②代表ArrayList的默认容量大小
③④均为空数组,区别具体在哪慢慢分析
⑤为ArrayList类在使用过程中调用的数组
⑥ArrayList有效元素个数
这里我们以无参构造方法来具体分析其扩容机制:
通过无参构造方法创建ArrayList时,默认将elementData数组引用DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组
继续往后看,分析源码是如何添加元素的
将带添加元素传入进来后,首先调用了ensureCapacityInternal函数,并且把size+1也就是将目前元素添加进数组后的长度作为实参传过去,我们猜测目的是判断是否超出当前长度是否需要扩容,那我们顺着往后分析;
通过这个函数又调用了ensureExplicitCapacity函数并且在其内嵌套了一个calculateCapacity函数,同时还将elementData数组,当前元素个数传入了进去我们继续分析calculateCapacity函数
刚刚在创建ArrayList时虽然引用了一个空数组,但是通过calculateCapacity中的if判断,如果elementData未开辟空间,则返回DEFAULT_CAPACITY, minCapacity的最大值,此时我们的最大值是DEFAULT_CAPACITY也就是默认数组大小10;我们回到calculateCapacity的函数调用者ensureExplicitCapacity函数
将calculateCapacity函数返回值与当前数组长度判断,如果大于0则调用grow函数,也就是我们说的扩容函数!接下来就具体看看grow函数到底卖的是什么葫芦~
首先oldCapacity存储了当前数组的长度,newCapacity:(扩容的大小/创建ArrayList的大小)
newCapacity这一行的操作是一个1.5倍扩容(这里解释一下oldCapacity >> 1正好就是原大小的0.5倍),if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
如果新容量大小小于我们要实现/添加的元素大小,则newCapacity = minCapacity,
if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
如果newCapacity大小大于了
则调用hugeCapacity
if (minCapacity < 0) // overflow throw new OutOfMemoryError();
判断minCapacity是否<0,如果小于0,则创建/扩容失败,抛出异常
判断minCapacity是否大于整数最大值,大于则返回整形最大值作为数组大小.
elementData = Arrays.copyOf(elementData, newCapacity);
最后,我们就实现好了一个ArrayList类
结论
1.检测是否需要扩容,需要则一路走下来调用我们的grow函数
2.计算扩容需要的大小:
- 初步预估按照1.5倍大小扩容
- 如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
- 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
- 使用copyOf进行扩容
思考
最后的最后,几个问题对我们的顺序表进行一个简单的提问
1.顺序表的中间/头部的插入,时间复杂度是多少?为什么?
2.增容对于资源的开销在哪些方面?
3.这样的增容方式合理么?如何解决?