从数据结构的角度来剖析ArrayList

前言

不管是最java后端还是android前端,说起ArrayList大家应该都是非常熟悉,因为你可能无时无刻不在用。可是小编还是要在这里啰嗦下它的原理,因为这个真的很重要,下面我以问答的方式来展开了讲。

1. 底层是如何实现的

回答这个问题,想必大家都知道答案,是的,ArrayList底层是数组的方式实现的,也可以说它就是实现了一种动态数组。那么我要问什么是数组,你又要如何解答呢?其实这个才是最关键的,也是这篇文章的重点。

数组是其实是一组连续的内存空间,用来存储一组相同类型的数据,它是一种线性表数据结构。因为这个特点,数组中每个元素的地址都可以使用基础地址与下标通过寻址公式快速计算出来。比如说数组中第一个元素的地址是1,每个元素占用的空间是3,那么第二个元素的地址是1+3=4,同理,第n个元素的地址是1+3*n。

2. 为什么说数组的查询快,插入和删除慢

首先快和慢我们可以通过时间复杂度来进行衡量,那么我们来分析下每一种操作的时间复杂度。

查询:上面说过数组是一组连续的内存空间,其中每个元素的内存地址是可以通过公式计算得到,有了内存地址,便可能拿到当前元素,这样看的话,它的时间复杂度便是O(1) 。

插入:还是从连续内存空间作为切入点,要往数组中插入一个元素,首先是要找到它的位置,这个跟上面查询的一样的,时间复杂度为O(1),这个时候如果插入的位置不在最后一个,那么当前位置上是一定有元素的,那就必须先把这个位置和它之后的所有元素向后移一位,理论上讲如果上向第0个位置插入元素的话,就得先移n个元素,这个时候时间复杂度就是O(n)。

删除:删除操作其实和插入操作是差不多的,因为数组是连续的内存空间,所以当某个位置的元素被删除时,就会不连续了,不符合数组的规范,这时就需要将后面的元素全部向前移一位,所以时间复杂度也是O(n)。

至此,这个问题的答案就一目了然了。

3. 如何扩容的

我们都知道数组在申明时都是要给一个长度的,ArrayList是基于数组,那么它在申明的时候也是需要一个长度,但是我们在平时用的时候可能都是直接new出来,并没有给定长度,细心的朋友如果去看源码的话会发现,其实内部是有一个默认长度的,DEFAULT_CAPACITY = 10,那么这时候就会存在一个问题,如果我放进去的元素超过10个,会怎么办呢?这个时候就得用到扩容了。

数组扩容是指当数组当前的长度不够用的时候,需要重新申明一块更大的连续的内存空间来存放,这里我主要说下扩容规则。

  1. 初始化的时候如果不传入长度的话,会先给了一个长度为0的数组,当第一次往集合中添加元素时,便会给数组初始化为默认长度10。
  2. 当元素添加个数超过10的话会重新计算出一个新长度,计算方法如下:newLength = oldLength+oldLength>>1 ,这里用到了位运算,大概结果就是原来长度的1.5倍,为什么说是大概结果呢,这就涉及到了位运算的基础知识,如果想了解可以看我的另一篇文章: 位运算详解与实战,个人认为讲解很详细,而且里面会有实战的运用。
  3. 得到了新的长度后会做一个校验,校验主要是为了让数组长度不要超过Integer.MAX_VALUE,也就是Int的最大值。

至此扩容的逻辑也将完了,给大家点建议,平时用的话如果能确定集合的长度,在初始化的时候最好的给个初始化值,这样能避免动态扩容。

4. 是否可以添加null

答案肯定是可以的,准确来说ArrayList是可以存放任何类型的元素,因为内部是一个Object[],只是通常用的时候都会要求指定一直类型,在这里泛型只是一种约束,只是为了将元素的类型约定为一样的。

5. 是否是有序的

这里我先来说下我们通常意义下理解的有序,应该就是取出来的顺序和存进去的顺序是一致的,也可以理解为队列,比如说往ArrayList中依次添加三个元素A,B,C,那么我们for循环取出来的顺序也是A,B,C。实现这个并不难,去到ArrayList的add()方法中就可以分析出,ArrayList中有一个size变量,用来保存当前元素的个数,当往ArrayList中添加一个元素时,其实是调用了elementData[size++] = e; 可以看到这里会先把数组后面的一位赋值给了当前元素,然后再将长度加1,这样就保证了数组的有序。

5. 优缺点

优点:查询快
缺点:增删慢,对内存要求高(因为需要连续的内存空间)

总结

总的来说ArrayList的原理还是比较简单的,核心地方就是理解数组的特性,理解了数组便可以自己分析出ArrayList的优缺点,这里扩容只是一种计算方法。本文讲的是基于java1.8,这个在不同版本中可能会有所差异,但核心实现是不会变的。了解了它的原理,大家可以私下自己实现一个ArrayList,以便加深理解。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值