ArrayList源码分析

本文详细分析了ArrayList的源码,包括ArrayList的基本概念、成员属性、构造方法、添加和删除元素的过程,重点讨论了ArrayList的扩容机制,解释了为何在添加元素时会触发扩容以及grow()方法的工作原理。此外,还提到了遍历ArrayList的方式和fail-fast机制,强调了在多线程环境下使用ArrayList需要注意的问题。
摘要由CSDN通过智能技术生成

文章放置于:https://github.com/zgkaii/CS-Notes-Kz,欢迎批评指正!

1. AzrrayList 简介

java.util.ArrayList 是一个数组队列,相当于 动态数组。与Java中的数组相比,它具有容量能动态增长、元素增删慢、查找快的特点。

ArrayList继承于 AbstractList,实现了 ListRandomAccessCloneablejava.io.Serializable 这些接口。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
   
		--- omit ---
  }
  • ArrayList 继承了AbstractList抽象方法,实现了List接口,提供了相关的添加、删除、修改、遍历等功能。
  • RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持快速随机访问的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
  • ArrayList 实现了 Cloneable 接口,即覆盖了函数clone(),能被克隆。
  • ArrayList 实现了java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。

Tips:ArrayList中的操作不是线程安全的!建议在单线程中才使用ArrayList,多线程中选择使用Vector或者CopyOnWriteArrayList

2. ArrayList源码简析(JDK 1.8)

2.1 成员属性

// 默认容量大小
private static final int DEFAULT_CAPACITY = 10;

// ArrayList空实例共享的一个空数组
private static final Object[] EMPTY_ELEMENTDATA = {
   };

// ArrayList空实例共享的一个空数组,用于默认大小的空实例。
// 与EMPTY_ELEMENTDATA分开,这样就可以了解当添加第一个元素时需要创建多大的空间。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
   };

// 真正存储ArrayList中的元素的数组
transient Object[] elementData;

// ArrayList 所包含的元素个数,注意并不是elementData的长度
private int size;

// 数组的最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

AbstractList抽象类中唯一属性:

// 表示elementData被修改的次数,每次add或者remove它的值都会加1
protected transient int modCount = 0;

为什么size不是用来标记elementData数组的长度呢?

在Java中,一般来说:

  • length属性是针对数组而言的,比如下面源码中elementData.length表示elementData数组的长度。
  • length() 方法是针对字符串而言的,可以调用 length() 获取字符串长度。
  • size() 方法是针对泛型集合而言的,可以调用size()来获取集合中个数。

这样,就很好理解为什么size不是ArrayList的长度了,不易记混。

2.2 构造方法

ArrayList有三种初始化方式:

    /**
     * 有参的构造函数
     */    
	public ArrayList(int initialCapacity) {
   
        if (initialCapacity > 0) {
   
            // 初始化容量大于0,创建initialCapacity大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
   
            // 初始化容量等于0,创建空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
   
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * 默认无参构造函数(未指定初始化容量大小),使用初始容量10构造一个空列表。
     */
    public ArrayList() {
   
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 使用Collection集合来初始化ArrayList
     */
    public ArrayList(Collection<? extends E> c) {
   
        // 将集合转换为数组
        elementData = c.toArray();
        // 如果数组的长度size不等于0
        if ((size = elementData.length) != 0) {
   
            // 如果返回的不是Object类型数据
            if (elementData.getClass() != Object[].class)
                // 重新创建一个size大小的数组。
                // 并将原来不是Object[]数组的内容,Copy给新的是Object[]的数组
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
   
            // 用空数组代替
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

从上面的分析中看出EMPTY_ELEMENTDATA与DEFAULTCAPACITY_EMPTY_ELEMENTDATA的区别

EMPTY_ELEMENTDATA表示在我们实例化对象时指定了容量就是0,当添加1个元素后,那么elementData.length=1。

DEFAULTCAPACITY_EMPTY_ELEMENTDATA表示实例化时是无参构造,未指定容量,在调用add方法添加第1个元素后会默认扩容容量为10,即elementData.length=10。

2.3 添加元素

    /**
     * 直接将元素添加到数组末尾。
     */
    public boolean add(E e) {
   
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值