1. ArrayList结构图

ArrayList 是 java 集合框架中比较常用的数据结构了。继承自 AbstractList,实现了 List 接口。底层基于数组实现容量大小动态变化。允许 null 的存在。同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速访问、复制、序列化的。
ArrayList - 图1

2. ArrayList类简介

  • 1、ArrayList是内部是以动态数组的形式来存储数据的、知道数组的可能会疑惑:数组不是定长的吗?这里的动态数组不是意味着去改变原有内部生成的数组的长度、而是保留原有数组的引用、将其指向新生成的数组对象、这样会造成数组的长度可变的假象。
  • 2、ArrayList具有数组所具有的特性、通过索引支持随机访问、所以通过随机访问ArrayList中的元素效率非常高、但是执行插入、删除时效率比较地下、具体原因后面有分析。
  • 3、ArrayList实现了AbstractList抽象类、List接口、所以其更具有了AbstractList和List的功能、前面我们知道AbstractList内部已经实现了获取Iterator和ListIterator的方法、所以ArrayList只需关心对数组操作的方法的实现、
  • 4、ArrayList实现了RandomAccess接口、此接口只有声明、没有方法体、表示ArrayList支持随机访问。
  • 5、ArrayList实现了Cloneable接口、此接口只有声明、没有方法体、表示ArrayList支持克隆。
  • 6、ArrayList实现了Serializable接口、此接口只有声明、没有方法体、表示ArrayList支持序列化、即可以将ArrayList以流的形式通过ObjectInputStream/ObjectOutputStream来写/读。

    基础属性

    ArrayList部分源码如下:
    image.png
    如上代码中为ArrayList的主要属性:

  • DEFAULT_CAPACITY:默认容量,即为初始值大小

  • EMPTY_ELEMENTDATA:共享的空数组,用于初始化空实例
  • elementData:ArrayList内部结构,是一个Object[]类型的数组
  • size:数组长度大小

    构造方法

    如下为ArrayList的构造方法:
    image.png

  • 1.构造方法1,表示接受指定地容量值,初始化创建数组,建议在可估算数组大小时,创建ArrayList可指定

  • 2.构造方法2,是默认的构造方法,它将创建一个空数组
  • 3.构造方法3,接收一个Collection的实体,将该Collection实体转换为ArrayList对象

    主干流程

    1.添加指定元素代码如下
    image.png
    可以看到实际上只有3行代码,其流程主要如下:

    1.扩容 (这里便解释了,在介绍时提出的问题):

    主要源码如下
    image.png

  • 第一个方法的逻辑为:判断是不是第一次添加元素,若为第一次,则设置初始化大小为默认的值10,否则使用传入的参数

  • 第二个方法的逻辑为:若长度大于数组长度,则扩容
  • 第三个方法的逻辑为:

image.png

2.赋值:将添加的值放置到size++的位置上

3.返回:返回true

2.添加指定元素到指定的位置上代码如下:
image.png
其流程为:

  • 1.校验下标:调用rangeCheckForAdd方法进行下标校验,不正确则会抛出IndexOutOfBoundsException异常
  • 2.扩容:详见上部分中做的介绍
  • 3.移动数据:将数据index后面的数据,都向后移动
  • 4.赋值:将加入的值放置到index位置中
  • 5.长度增加:长度增加

    3. 常见问题

    1.问题描述

    在使用ArrayList比较常见的一个问题就是在遍历ArrayList的时候调用remove()方法进行元素的删除操作,从而得到意想不到的结果,本人在开发过程中也遇到过这样的问题,所以在这里提出了,希望能够帮助到大家。

    2.实例及分析

    如下代码中,在遍历List时,调用了remove方法,删除元素a
    image.png

  • 这段代码看似解决了删除列表中所有的a元素,但是删除后得出List的结果为[a, c, a],为什么这种方式没有达到想要的效果,其实仔细分析后会发现,在调用remove()方法时List的长度会发生变化而且元素的位置会发生移动,从而在遍历时list实际上是变化的,例如

  • 当i=0时,此时list中的元素为[a,a,c,a,a],
  • 但当i=1时,此时List中的元素为[a,c,a,a],元素的位置发生了移动,从而导致在遍历的过程中不能达到删除的效果

    3.解决方案

    通过上述的分析可以看出,出现问题的原因是元素的位置发生了移动,从而导致异常的结果 方案一、逆向遍历List删除,代码如下,这种做法可行主要是因为remove()方法删除index处的元素时,是将index+1到size-1索引处的元素前移,而逆向遍历可以避免元素位置的移动
    image.png
    方案二、使用迭代器中的remove方法,迭代器具体参考Iterator详解,主要代码如下(这种方式比较推荐)
    image.png

    4. 手写一个ArrayList

    自己手写一个ArrayList,代码如下:
    image.png
    image.png
    image.png

    5. 总结(重要)

    image.png