[手撕源码]ArrayList与顺序表分析

文章重点总结我在学习中遇到的不易理解的点,以及在面试过程中常遇到的点和题目思路分享.

👉文章默认大家拥有ArrayList和LinkedList的基础知识

基础回归:
⁜ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表

一.分析ArrayList的源代码

1.ArrayList的构造

image-20220821225454458

通过分析ArrayList类下一共有三种构造方法

image-20220821230012665无参构造方法
image-20220821230029375指定顺序表初始容量
image-20220821230120859利用其他 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() + " ");
}

迭代器工作示意图:

1661096025312 00_00_00-00_00_30

3.面试高频题:ArrayList的扩容机制

下面将通过分析java底下的源代码进行分析:

image-20220821233851931

简单解释上述编号代码的代码含义:

②代表ArrayList的默认容量大小
③④均为空数组,区别具体在哪慢慢分析
⑤为ArrayList类在使用过程中调用的数组
⑥ArrayList有效元素个数

这里我们以无参构造方法来具体分析其扩容机制
image-20220821234217160

通过无参构造方法创建ArrayList时,默认将elementData数组引用DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组

继续往后看,分析源码是如何添加元素的

image-20220821234605711

将带添加元素传入进来后,首先调用了ensureCapacityInternal函数,并且把size+1也就是将目前元素添加进数组后的长度作为实参传过去,我们猜测目的是判断是否超出当前长度是否需要扩容,那我们顺着往后分析;

image-20220821234830144

通过这个函数又调用了ensureExplicitCapacity函数并且在其内嵌套了一个calculateCapacity函数,同时还将elementData数组,当前元素个数传入了进去我们继续分析calculateCapacity函数

image-20220821235018272

刚刚在创建ArrayList时虽然引用了一个空数组,但是通过calculateCapacity中的if判断,如果elementData未开辟空间,则返回DEFAULT_CAPACITY, minCapacity的最大值,此时我们的最大值是DEFAULT_CAPACITY也就是默认数组大小10;我们回到calculateCapacity的函数调用者ensureExplicitCapacity函数

image-20220821235406283

将calculateCapacity函数返回值与当前数组长度判断,如果大于0则调用grow函数,也就是我们说的扩容函数!接下来就具体看看grow函数到底卖的是什么葫芦~

image-20220821235556135

首先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大小大于了image-20220822000438772

则调用hugeCapacity

image-20220822000506606

 if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();

判断minCapacity是否<0,如果小于0,则创建/扩容失败,抛出异常

判断minCapacity是否大于整数最大值,大于则返回整形最大值作为数组大小.

 elementData = Arrays.copyOf(elementData, newCapacity);

最后,我们就实现好了一个ArrayList类

结论

1.检测是否需要扩容,需要则一路走下来调用我们的grow函数

2.计算扩容需要的大小:

  • 初步预估按照1.5倍大小扩容
  • 如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
  • 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
  1. 使用copyOf进行扩容

思考

最后的最后,几个问题对我们的顺序表进行一个简单的提问

1.顺序表的中间/头部的插入,时间复杂度是多少?为什么?

2.增容对于资源的开销在哪些方面?

3.这样的增容方式合理么?如何解决?

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XY枫晨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值