【源码学习】 深入剖析核心源码之 ArrayList

学习一段时间后看源码是必不可少的一步,源码的学习通常是将常用的方法的实现理解透彻并能合理的使用,掌握其特性。对于Java需要掌握的就是各种容器,今天首先开始学习了ArrayList的源码,将自己的学习记录下来,接下来会陆续学习剩下的源码

一、ArrayList基础知识

  1. ArrayList 的底层是基于数组实现,且初始数组为空。由于被final和static修饰,该成员变量不可以被更改也属于类。在ArrayList中可以装载对象,其中当我们装载的是:int、byte、short、long、float、char 等基本数据类型时,实际上我们是存储了它们对应的包装类。其底层是通过Object[] elementData实现的。

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200204215746957.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01vb19MYXZlbmRlcg==,size_16,color_FFFFFF,t_70

  1. ArrayList 如构造时不指定参数,数组的默认容量为 10。在使用时我们可以通过 size 这个私有的成员变量得到这个容器中的元素容量。

在这里插入图片描述
在这里插入图片描述

  1. ArrayList是线程不安全的,线程安全版本的数组容器是Vector,可以看作是将 ArrayList的所有方法都加上synchronized 关键字。
  2. 同样是遍历整个所有元素,ArrayList会比LinkedList快,因为ArrayList的元素占用的是连续的物理内存,方便CPU读取数据

二、常用方法实现

在这里插入图片描述

1. 构造方法

在源码中提供了两种构造方法,分别为无参的和有参的。

  • 在无参的构造函数中,将该容器的底层数组引用elementalData指向未初始化的数组的成员变量。此时底层数组长度默认为10。在这里插入图片描述
  • 在有参的构造函数中,传入的initialCapacity是底层数组elementalData的长度,如果传入的参数是负数,则会抛出异常。如果传入容量是0,则引用类中的一个空的对象,若容量大于0,则构造一个该长度的相应对象类型的数组。
    在这里插入图片描述

注意!这里有一个易混淆的点就是——无论哪种构造函数都不会初始化ArrayList的大小
源码之中表示的大小是length,也就是底层数组elementalData的长度,而ArrayList的大小是size,是多次调用add方法加入元素的个数。所以初始化指定的大小是数组的大小而非ArrayList的大小。
看一个例子来直观理解一下:
若我们不添加元素就调用size()方法,得到的结果为0。若调用get(0)set(0,1)等方法,都会抛出java.lang.IndexOutOfBoundsException的异常表示数组越界。
在这里插入图片描述

2. ensureCapacity(int minCapacity)

通过调用该方法,来保证ArrayList实例的容量。其中minCapacity可以看作所需的最小容量,minExpand就是当前容器的初始化容量。如果所需要的容量更大些,就调用方法进行扩容。

在这里插入图片描述
接下来就是调用了ensureExplicitCapacity(int minCapacity)方法,该方法判断所需要的容量如果比当前容器的容量大,说明容量不够,就调用grow(minCapacity)方法扩容。
在这里插入图片描述
再进行计算新的ArrayList容器的长度时,使用的位运算的右移运算就是除以2。新的容器长度为旧长度的1.5倍。而扩容也是一个简单的过程:申请新数组 ,调用Array类中的copyOf方法将旧数组中的元素拷贝到新数组中。
在这里插入图片描述

3.contains(Object o)

该方法用于判断元素O是否存在于该容器中,在方法中再次调用indexOf(Object o)方法,来判断其返回值。而indexOf(Object o)方法是返回指定元素在该列表中第一次出现的位置索引。其实现方法也很简单,只是对进行整个ArrayList 中元素进行遍历。
在这里插入图片描述

4.size()

返回私有的成员属性 size,这样可以确保size属性不轻易被修改。
在这里插入图片描述

5. isEmpty()

判断ArrayList容器是否为空,函数中为判断size的值是否为0
在这里插入图片描述

6.toArray()

该方法通过数组的拷贝来返回包含所有容器中元素的数组。
在这里插入图片描述

7.get(int index)

首先检查要获取下标的合法性,然后直接根据索引访问
在这里插入图片描述

8. set(int index, E element)

该方法把指定的index下标的元素进行修改为element,首先判断下标是否合理,后保存原来的值修改后返回。
在这里插入图片描述

9.add(E e)

  • 尾插操作:对ArrayList中进行插入新的元素时,首先一定要判断在新增一个元素后容量是否够,若不够就进行扩容操作,然后再尾插进元素e
    在这里插入图片描述
  • 指定位置插入:重点操作就是复制,调用arraycopy(Object src, int srcPos, Object dest, int destPos, int length)方法将底层数组elementDataindex位置到结尾的元素拷贝到index + 1 位置处,为element留出index处的位置,再进行修改该处的元素为element
    在这里插入图片描述

10.remove(int index)

通过阅读源码我们可以发现,删除方法也是通过数组拷贝实现的。删除事实上也可以看成是将index位置处的元素覆盖了,arraycopy实现的功能就是将index+1位置以后的元素全部向前“移动”一位后,将最后一位的元素置为空,且size--
在这里插入图片描述

11.clear()

清空所有的元素,就是遍历然后将每一个元素都置为null,且zise置为0
在这里插入图片描述

有任何问题欢迎小伙伴们随时批评指正

发布了62 篇原创文章 · 获赞 28 · 访问量 6049

猜你喜欢

转载自blog.csdn.net/Moo_Lavender/article/details/104175629