三顾ArrayList

73 篇文章 0 订阅
73 篇文章 0 订阅

 ArrayList部分一共五篇文章了,并且引入了时间复杂度来分析,强烈建议大家一定要按顺序阅读,相关文章分别是:

1、《ArrayList初始化 - Java那些事儿》

2、《ArrayList底层数组扩容原理 - Java那些事儿》

3、《时间复杂度 - Java那些事儿专栏》

4、本文

第三次强调,ArrayLIst是一个普通的类

好,现在我们来讨论一下数组的删除,我们知道数组一但在堆内存中创建出来,数组长度是不可变的,看以下源码:

 

添加10个用户

 

比如我们要把“周八”这个人从数组中删除,如图:

 

我们只能循环数组,找到“周八“的下标5,由于数组没有提供删除方法,我们只能把下标为5的位置赋值为null(造成了数组空洞),“周八”这个Person对象已经没有引用指向它了,JVM的垃圾回收机制会在适当的时候回收它。但数组的长度还是10。下次当我们再循环查找某人时,稍不注意就会报空指针异常,虽然我们可以写非空去判断,但还是不太友好,我们把null后面的所有元素引用复制一下,往前拷贝一份,把null这个空给填上,如下图

 

复制后:

 

null之后的ref引用都按顺序复制了一份到原来的null的位置,原有的1引用被覆盖,但perArr[9]里的引用的指向还是不变(注意,是复制不是挪动,仔细看一下上面两个图)。
注意:perArr[8],perArr[9]指向的是同一个对象,这显然不是我们所要的结果,再处理一下,我们把perArr[9]的引用赋值为null。如下图:

 

问题似乎解决了,但数组长度还是10,还需要自行维护了一个size来记录长度,以上数组复制的代码,我们都要自己去写好在ArrayList这个类已经实现了,数组拷贝工作交给它就好,我们只需要调用ArrayList这个类提供的remove删除元素就行,至于底层数组怎么拷贝,元素怎么删除由ArrayList对象本身去搞定(面向对象的思想),我们来看一看ArrayList的两种元素删除方式,首先是按照下标删除:

 

我们先看看删除前的元素,debug一下:

 

perList里面已经有了10个元素,执行一下这两句remove操作,再看一下debug的情况

 

下标为5的“周八”已经删除掉了,下标为5以后的元素也按照我们之前的猜想往前移了一位,数组最后一个位置也置为null了。奇怪!“孙七”居然没有删掉!打印出来的个数也是9

 

我们看一下两种删除方式的源码。

 

基本上和我们图中的分析一致,并采用size来记录元素的真实个数,这段代码里还调了一个方法rangeCheck()方法,我们看一下:

 

好简单对不对,就是检查底层数组下标是否越界。我们再看另外一种删除方式

 

再看一下fastRemove()方法

 

和上面用下标删除方式一致,这儿就不细说了。

相信大家看到上面熟悉的equals()方法,就大概知道“孙七”为什么没有删掉了,如果你写了一个类(Person),你需要这个类完美的支持List,你必需按照List的规范来写代码,我们在
说说Java里的equals(中) - Java那些事儿专栏 一文中已经说得很清楚了,这儿就不细说了。

 

知道问题的原因就好解决了,我们重写equals()方法试一下。

 

重写完equals方法,执行一下再debug看一下

 

孙七已经删除掉了,孙七后面的所有人也向前复制了一格,末位置为null,size也是8了,再画一画图:

 

​图中的“孙七”,“周八”已经没有引用指向它们,JVM虚拟机会在适当的时候进行回收。

我们说一说ArrayList中删除元素的时间复杂度在ArrayLIst中,如果底层数组长度为n。

当我们用下标方式去删除元素时,如果删除的是最后一个元素,不会触发数组底层的复制,时间复杂度为O(1)。如果删除第i的元素,会触发底层数组复制n-i次,根据最坏情况,时间复杂度为O(n)。

由此看来,在ArrayList中删除指定元素的效率似乎不是太高,删除元素会造成底层数组复制,这个问题在LinkedList有方案解决,请关注后续专栏文章。

示例中,用对象的方式来删除元素,只是想告诉大家,这种删除方式是用equals方法来查找元素的下标进而删除的,实际工作中很少遇到需要new一个对象去删除的情况。 不建议一上来就重写equals方法,除非你有特殊的需求。如果重写了equals方法,请一并重写hashCode方法,这个问题在说说Java里的equals(中) - Java那些事儿专栏 一文中已经说过了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值