ArrayList源码分析——学习Java集合的必修课

文章详细介绍了Java中ArrayList的数据结构,包括它是基于动态数组实现,以及默认容量、元素存储方式。接着讨论了ArrayList的增删改查操作,特别是添加和移除元素的实现细节。此外,文章还提到了ArrayList的非线程安全特性,并阐述了迭代器的实现和fail-fast机制,帮助读者理解ArrayList的工作原理。
摘要由CSDN通过智能技术生成

前言

  Java中的ArrayList是开发者在日常使用中非常便利的一种数据结构,也是Java中最常用的数据结构之一。此文章将详细介绍ArrayList类的源码实现,包括ArrayList的数据结构、增删改查的操作实现、线程安全的实现、迭代器的实现,以及fail-fast机制。

数据结构

  ArrayList是基于动态数组实现的数据结构,内部使用数组存储元素。可以看下面的源代码(省去一些注释和文档说明):

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // 默认容量
    private static final int DEFAULT_CAPACITY = 10;

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

    // 存储元素的数组
    transient Object[] elementData;

    // ArrayList中元素的个数
    private int size;

    // 构造函数
    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);
        }
    }

    public ArrayList() {
        this.elementData = EMPTY_ELEMENTDATA;
    }

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

    // ...
}

  在 ArrayList 的头部,我们可以看到它实现了 List 和 RandomAccess 接口,并继承了 AbstractList。ArrayList 中的成员变量包括三个:elementData、size 和 DEFAULT_CAPACITY。

  elementData 存储的是 ArrayList 中的元素,这里的数据类型是 Object[] 数组,因此它可以存储任意类型的对象(基本数据类型需要通过包装类转换)。ArrayList 中 size 表示当前其中元素个数,DEFAULT_CAPACITY 表示默认容量大小为10,也就是说上代码中定义了初始容量为10,当添加第11个元素时,数组会自动进行扩容。

增删改查的操作实现

添加元素

  我们先来看 ArrayList 中添加元素的一些重载方法的源码:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
            size - index);
    elementData[index] = element;
    size++;
}

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount

    int numMoved = size - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                numMoved);

    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

  可以发现,在添加元素之前,ArrayList 内部调用了 ensureCapacityInternal() 进行扩容操作,以保证足够的容量来存储新的数据。如果在 ArrayList 头部添加元素,由于每个元素都要向后移动一位,因此效率较低。

获取元素

  获取元素在 ArrayList 中主要有两个方法:get(int index) 和 toArray(T[] a) 方法。

public E get(int index) {
    Objects.checkIndex(index, size);
    return elementData(index);
}

private E elementData(int index) {
    return (E) elementData[index];
}

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

  get() 方法主要是通过数组下标获取元素,toArray() 方法则是将 ArrayList 转换为数组。

移除元素

  ArrayList 中有三个方法可以用来移除元素:remove(Object o)、remove(int index) 和 removeAll(Collection<?> c)。

public boolean remove(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i] == null) {
                fastRemove(i);
                return true;
            }
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i])) {
                fastRemove(i);
                return true;
            }
    }
    return false;
}

private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                numMoved);
    // Let gc do its work
    elementData[--size] = null; // clear to let GC do its work
}

public E remove(int index) {
    Objects.checkIndex(index, size);
    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 removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}

  注意,每次移除元素之后,ArrayList 的 size 属性会减1。

线程安全的实现

  ArrayList 并不是线程安全的数据结构,在多线程情况下需要注意并发访问可能引起的问题。如果需要线程安全的操作,可以考虑使用 Collections.synchronizedList 或者 CopyOnWriteArrayList 等线程安全的集合类。

迭代器的实现

  ArrayList 实现了迭代器接口 Iterator。

public Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E> {
    int cursor;       // 下一个要被返回的元素的索引
    int lastRet = -1; // 上一个被返回的元素的索引(即上一个返回的元素)
    int expectedModCount = modCount;

    // ...

    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    // ...

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

  可以看到,在迭代 ArrayList 过程中,Itr 对象中包含了 cursor、lastRet 和 expectedModCount 三个成员变量,分别表示下一个要被返回的元素索引、上一个被返回的元素索引和 modCount 的期望值。在 next() 方法中,每次返回一个元素之前都会检查 modCount 是否和期望值相同,如果不同则抛出 ConcurrentModificationException 异常。

fail-fast 机制

  如果在迭代 ArrayList 过程中对其进行修改操作(如添加或删除元素),就会抛出 ConcurrentModificationException 异常,这是因为 ArrayList 内部实现了 fail-fast 机制,以保证迭代器遍历时不被干扰。

总结

  本文分析了 ArrayList 的数据结构、增删改查的操作实现、线程安全的实现、迭代器的实现,以及 fail-fast 机制。ArrayList 是一种非常实用的数据结构,了解其源码实现有助于我们更好地理解 Java 集合框架的设计思想和原理,并能够优化开发过程中的代码实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值