百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

java源码分析(一):快速掌握ArrayList

toyiye 2024-09-19 04:49 5 浏览 0 评论

今天给大家分析一下java中ArrayList的源码结构,由于此类函数较多,我们只分析重点部分,其余的请大家自行阅读源码.

ArrayList是一个数组结构的集合,也就是说底层存储结构是依赖数组的,那从性能方面讲:这种结构的集合在读取的时候速度非常快,因为有索引,但是修改的操作就比较慢了,这应该是大家都知道的,下面咱们正式来看代码:

首先看一下类结构:

ArrayList使用泛型定义,并且实现了标准的集合接口List,RandomAccess(这个接口需要解释一下,这是个随机访问的接口,他没有任何实现,是一种接口标记的行为,通常是为了提升集合在遍历时的性能,通常在进行集合遍历的时候使用instanceOf RandomAccess 来判断一下是否实现了这个接口,是的话用for循环,否则用迭代器),Cloneable(这个接口没有任何实现,标识当前实现类能够进行复制操作的一种能力,实现此接口的类需要重写clone()方法,但是此处需要注意的是copy操作分为浅拷贝或者深拷贝,这里简单做一下区分,浅拷贝是只把当前对象的属性进行copy,如果对象中有关联其他的对象,则这个对象不会进行copy.深拷贝:将所有属性统统进行copy,但是开销比较大),Serializable 序列化接口,没什么好解释的

分析一下属性:

下面说一下构造方法:

这个构造方法接受一个初始化长度,如果大于0则重新new一个Object数组指定一个长度,赋值给elementData,如果等于零则把elementData的指针指向那个空的默认数组

这个构造方法接收一个Collection的子类,首先调用toArray()将其转换成数组并赋值给elementData,然后进行判断如果长度等于零则直接将elementData的指针指向默认的空数组,如果不等于零则在进行一次判断他的类型是不是Object[],如果不是就进行一次拷贝,这个拷贝方法最后调用System.arraycopy()这个native方法

下面说一下add():

add()方法会先调用ensureCapacityInternal(size+1) 这个方法,参数是当前数组长度+1,ensureCapacityInternal()这个方法接受一个最小容量的数值,就是当前数组长度+1的长度 , 然后会判断当前elementData是否与DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个数组相等,相等则在默认长度和当前长度中挑选一个大的出来,紧接着调用这个方法:

这个操作首先会将modCount做++,这个变量是继承自AdstractList的,至于这个变量的作用,咱们在遍历的时候说, 下面if判断当前的容量 - 当前数组的长度 是否大于0 ,也就是说是否需要扩容,如果大于0就是要扩容,咱们再看看一下grow()方法:

这里我逐行解释:

int oldCapacity = elementData.length; //获取当前数组长度

int newCapacity = oldCapacity + (oldCapacity >> 1); //这是扩容之后的长度

if (newCapacity - minCapacity < 0) //判断新长度减去当前长度是否大于9

newCapacity = minCapacity; //将长度同步

if (newCapacity - MAX_ARRAY_SIZE > 0) //判断新长度是否超出了int的最大长度-8( Integer.MAX_VALUE - 8)

newCapacity = hugeCapacity(minCapacity); //调用hugeCapacity()方法,这个方法返回是这样的 :(minCapacity > Integer.MAX_VALUE - 8) ? Integer.MAX_VALUE : Integer.MAX_VALUE - 8;

最后返回add()方法继续执行 elementData[size++] = e; return true; 到这里这个方法就结束了.但是add()方法可以在置顶位置插入,继续往下看:

这个方法上来会进行一个检查,判断当前要插入的位置是不是会有下标越界,接下来 System.arraycopy(elementData, index, elementData, index + 1,size - index);这个操作会在需要插入的位置上整体往后移动,将这个位置空出来,然后进行赋值.

ArrayList也支持添加一个集合:这个操作比较简单,先进行数组长度的计算以及是否需要扩容,也就是调用ensureCapacityInternal()这个方法,其次是直接进行复制,将集合的内容复制到elementData里

接下来说一下删除操作,这个是根据下标来操作,性能开销就在arraycopy()这个操作上:

首先调用rangeCheck()方法判断下标是否越界,然后modCount做++,elementData()这个方法返回当前index的数据,numMoved 是elementData的长度减去要删除的位置-1,就是index的前一个位置,然后整体数组往前移动一位,这样最后一个位置就空出来了.然后调用elementData[--size] = null; 让GC来回收掉.

再看下根据元素来删除:

这个删除操作就是依赖for循环,在调用fastRemove()这个方法时:

所有的修改操作都会使modCount++,然后这个删除也是将数组往前移动一个位置,最后进行GC回收

最后说一下遍历,出了for循环依次遍历,还有一个迭代器,这个比较麻烦,一步一步说:

这两个方法都会初始化一个ListItr的对象,区别是一个带参数一个不带参数,参数的意思是从什么位置开始进行迭代,下面去看一下ListItr(内部类):

他继承了Itr这个内部类,并且实现了ListIterator这个接口,这个接口的所有方法又Itr和ListIterator这两个内部类一起实现,这里在迭代的时候只需要调用Itr类中的hasNext()方法来判断是否还有后续数据,通常作为while的条件.先看一下hasNext():

游标不能与当前数组长度就返回true,证明还有数据可以迭代.游标会在创建ListItr的时候声明,下面看一下next()这个方法:

这里就要提到modCount这个变量了,他是用来记录操作次数的,那为什么要记录呢.去看一下checkForComodification()这个方法:

之前讲过,每一次对集合的修改操作,都会触发modCount++,所以一旦出现多线程操作,在遍历过程中集合出现了修改操作,if (modCount != expectedModCount)这个操作肯定是不相等了,那就一定会报错了,那应该怎么避免这种情况呢? 答案是:CopyOnWriteArrayList,这个类是具有线程并发特性的,因为他使用了ReentrantLock这个锁,所以同步访问和修改都不会有问题.下面继续说next()这个方法.ListItr()构造方法会有一个赋值游标的操作:cursor = index; 这个index就是从什么位置开始遍历的下标,默认是0,if (i >= size) throw new NoSuchElementException(); 游标必须小于当前数组中元素的个数,否则会报错.

这个同样是判断游标长度是否超出数组下标长度的,然后cursor = i + 1;return (E) elementData[lastRet = i]; 游标往后走,返回取到的数据,这个lastRet 变量的初始值为-1

这样就将核心的几个方法都说完了,由于方法比较多, 写起来篇幅太长,所以只找了一个比较常用的来讲,其他的可以自己去看一下源码,比较易懂

喜欢的请关注一下,有问题欢迎留言或者私信哈.祝生活愉快.

相关推荐

Asterisk-ARI对通道中的DTMF事件处理

Asterisk通道中关于DTMF处理是一个非常重要的功能。通过DTMF可以实现很多的业务处理。现在我们介绍一下关于ARI对通道中的DTMF处理,我们通过自动话务员实例来说明Asterisk如何创建一...

PyQt5 初次使用(pyqt5下载官网)

本篇文章默认已安装Python3,本篇文章默认使用虚拟环境。安装pipinstallPyQt5PyQt一些图形界面开发工具QtDesigner、国际化翻译工具Liguist需要另外...

Qt开发,使用Qt for Python还是Qt C++ Qt开发,使用Qt for

Qt开发使用QtforPython还是QtC++?1.早些年写过一个PyQt5的项目,最近几年重构成QtC++了,其中有个人原因,如早期代码写得烂,...

最简单方法!!用python生成动态条形图

最近非常流行动态条形图,在B站等视频网站上,此类视频经常会有上百万的播放量,今天我们通过第三方库:bar_chart_race(0.2版本)来实现动态条形图的生成;生成的效果如图:问题:...

Asterisk通道和ARI接口的通信(aau通道数)

Asterisk通道和ARI详解什么是通道Asterisk中,通道是介于终端和Asterisk自己本身的一个通信媒介。它包含了所有相关信息传递到终端,或者从终端传递到Asterisk服务器端。这些信...

Python GUI-长链转短链(长链接转化成短链接java)

当我们要分享某一个链接给别人,或是要把某个链接放入帖子中时,如果链接太长,则会占用大量空间,而且很不美观。这时候,我们可以结束长链转短链工具进行转换。当然可以直接搜索在线的网站进行转换,但我们可以借此...

Python 的hash 函数(python的hash函数)

今天在看python的hash函数源码的时候,发现针对不同的数据类型python实现了不同的hash函数,今天简单介绍源码中提到的hash函数。(https://github.com/pyth...

8款Python GUI开源框架,谁才是你的菜?

作为Python开发者,你迟早都会用到图形用户界面来开发应用。本文千锋武汉Python培训小编将推荐一些PythonGUI框架,希望对你有所帮助。1、Python的UI开发工具包Kivy...

python适合开发桌面软件吗?(python可不可以开发桌面应用软件)

其实Python/Java/PHP都不适合用来做桌面开发,Java还是有几个比较成熟的产品的,比如大名鼎鼎的Java集成开发环境IntelliJIDEA、Eclipse就是用Java开发的,不过PH...

CryptoChat:一款功能强大的纯Python消息加密安全传输工具

关于CryptoChatCryptoChat是一款功能强大的纯Python消息加密安全传输工具,该工具专为安全研究专家、渗透测试人员和红蓝队专家设计,该工具可以完全保证数据传输中的隐私安全。该工具建立...

为什么都说Python简单,但我觉得难?

Python普遍被大家认为是编程语言中比较简单的一种,但有一位电子信息的学生说自己已经学了C语言,但仍然觉得Python挺难的,感觉有很多疑问,像迭代器、装饰器什么的……所以他提出疑问:Python真...

蓝牙电话-关联FreeSwitch中继SIP账号通过Rest接口

蓝牙电话-关联FreeSwitch中继SIP账号通过Rest接口前言上一篇章《蓝牙电话-与FreeSwitch服务器和UA坐席的通话.docx》中,我们使用开源的B2B-UA当中经典的FreeSWIT...

技术分享|Sip与WebRTC互通-SRProxy开源库讲解

SRProxy介绍目前WebRTC协议跟SIP协议互通场景主要运用在企业呼叫中心、企业内部通信、电话会议(PSTN)、智能门禁等场景,要想让WebRTC与SIP互通,要解决两个层面的...

全网第N篇SIP协议之GB28181注册 JAVA版本

鉴于网上大部分关于SIP注册服务器编写都是C/C++/python,故开此贴,JAVA实现也贴出分享GB28181定义了了基于SIP架构的视频监控互联规范,而对于多数私有协议实现的监控系统...

「linux专栏」top命令用法详解,再也不怕看不懂top了

在linux系统中,我们经常使用到的一个命令就是top,它主要是用来显示系统运行中所有的进程和进程对应资源的使用等信息,所有的用户都可以使用top命令。top命令内容量丰富,可令使用者头疼的是无法全部...

取消回复欢迎 发表评论:

请填写验证码