ArrayList源码解析

本文详细解析了Java中ArrayList的工作原理,包括其内部数据结构、扩容机制、添加与删除元素的过程等核心内容,并探讨了如何确保线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 引言

上个月去一家公司面试 java 实习生,面试官说的一句话我记得很清楚 作为一个java 工程师,你不去看源码是很难提高的。通过看源码,不仅可以更快的解决问题,而且可以直接接触到大牛写的代码,了解他们的设计思想,是有很大的好处的。 虽然后面由于种种原因把实习给辞了,不过面试官给的建议我认为是有用的,从今天开始了解一下 JDK 中集合框架的源码,以此提升自己。

查看 JDK 版本为 1.8。另外,由于本人只是水平有限,因此博客中有问题欢迎大家指出。

2. 概要

ArrayList 底层的数据结构很简单,就是利用数组来完成的。在源码中是这样定义的:transient Object[] elementData; ,根据构造函数的不同,其初始化方式也不同。其默认大小为 10。

这里为什么要使用 transient 关键字修饰呢?通过网上查找资料了解到,elementData 数组中的元素并不总是满的,例如数组长度为 10 而存储的元素只有五个。ArrayList 自定义了自己的 wirteObject 方法,保证只有这五个元素被序列化。

还需要注意的一点是,ArrayList 不是线程安全的,在多线程条件下,可以考虑使用 Collections.synchronizedList(List list) 来返回一个线程安全的 ArrayList。也可以使用 concurrent 包下的 CopyOnWriteArrayList 类。

3. 常用 API

3.1 扩容

ArrayList 底层虽然是使用数组存储数据,但我们并不需要担心会发生数组越界的情况。每次添加元素时,内部都会执行一个方法 ensureCapacityInternal(int minCapacity) ,传入的参数为当前数组长度+1,即现在 ArrayList 需要的最小容量,判断添加一个元素之后是否会存在数组越界的情况。

private void ensureCapacityInternal(int minCapacity) {
   if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

ensureExplicitCapacity 函数中,比较 ArrayList 需要的最小容量和当前数组的长度大小,判断是否进行扩容,如果需要的话,则调用 grow 方法。通过查看源代码,发现,它扩容后的大小一般为原数组的 1.5 倍。当然数组的长度也是有限的,它的最大长度为 Integer.MAX_VALUE - 8

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}
3.2 添加元素

了解了 ArrayList 的扩容过程,那么添加元素这个函数就变得简单了。其实现如下,其中 size 表示当前 ArrayList 元素的个数,添加元素直接对下标为 index 的位置赋值。

public boolean add(E e) {
     ensureCapacityInternal(size + 1);  
     elementData[size++] = e;
     return true;
 }
3.3 删除元素

在 ArrayList 中,有两种删除元素的方法

一个是通过角标:public E remove(int index)。其思路是找到指定位置,通过 Systemarraycopy 方法将下标 index 后的元素复制到原数组中直接覆盖下标为 index 的元素,以此达到删除的效果。

 public E remove(int index) {
     rangeCheck(index);

     modCount++;
     E oldValue = elementData(index);

     int numMoved = size - index - 1;
     if (numMoved > 0)
         System.arraycopy(elementData, index+1, elementData, index,
                          numMoved);
     elementData[--size] = null; // clear to let GC do its work

     return oldValue;
 }

另一个是直接删除指定的元素:public boolean remove(Object o)。删除指定元素,第一步先判断 o 对象是否为 null,如果是的话,删除数组中值为 null 的元素,如果不是,则删除指定的元素。其最终删除的方法也是通过赋值数组来完成,这里就不再重复。

3.4 修改元素、查找元素

修改元素与查找元素实现的原理差不多,其中应该注意的是在修改或者查找元素之前,应该先判断下需要查找的下标是否超出数组的长度,如果是的话,就应该抛出一个异常。ArrayList 是通过 rangeCheck 来判断的。

public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}
ArrayListJava集合框架中的一个类,它实现了List接口,可以用来存储一组对象,这些对象可以是任意类型。 下面是ArrayList源码解析: 1. 成员变量 ```java /** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size; ``` ArrayList有三个成员变量,分别是DEFAULT_CAPACITY、EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA。DEFAULT_CAPACITY表示默认的容量大小,EMPTY_ELEMENTDATA是一个空数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA也是一个空数组,但它会在第一次添加元素时扩容为DEFAULT_CAPACITY大小。elementData是一个Object类型的数组,用于存储ArrayList中的元素,size表示ArrayList中元素的数量。 2. 构造方法 ```java /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // defend against c.toArray (incorrectly) not returning Object[] // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } } /** * Constructs an empty list with the specified initial capacity. * * @param initialCapacity the initial capacity of the list * @throws IllegalArgumentException if the specified initial capacity * is negative */ public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } ``` ArrayList提供了三个构造方法。第一个构造方法是无参的构造方法,它将elementData赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA。第二个构造方法接收一个Collection类型的参数c,它将参数c中的元素转为数组并将其赋值给elementData。第三个构造方法接收一个int类型的参数initialCapacity,它根据参数initialCapacity的值创建一个Object类型的数组并将其赋值给elementData。 3. 常用方法 常用方法包括add()、get()、set()、remove()、size()等。 add()方法用于在ArrayList中添加一个元素,如果elementData的容量不足,就需要进行扩容。扩容的方式是将elementData数组的大小增加50%。 get()方法用于获取ArrayList中指定位置的元素。 set()方法用于将ArrayList中指定位置的元素替换为指定的元素。 remove()方法用于删除ArrayList中指定位置的元素。 size()方法用于获取ArrayList中元素的数量。 4. 总结 ArrayListJava集合框架中的一个类,它实现了List接口,可以用来存储一组对象。ArrayList源码解析包括成员变量、构造方法和常用方法。掌握ArrayList源码可以帮助我们更好地理解它的实现原理,从而更加灵活地应用它。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值