JDK ArrayList底层源码分析

27 篇文章 0 订阅

ArrayList是Java集合框架中常用的数据结构之一,它可以动态地增加或删除元素,并且支持随机访问和快速插入、删除操作。本文将深入分析ArrayList的底层源码实现,帮助读者更好地理解ArrayList的工作原理。

ArrayList类的概述

ArrayList是Java集合框架中的一个类,它继承了AbstractList类,实现了List接口,可以用来存储任意类型的对象。它的底层实现基于数组,支持随机访问和快速插入、删除操作。

在ArrayList类中,有两个重要的成员变量:elementData和size。elementData是一个Object类型的数组,用来存储ArrayList中的元素;size是一个int类型的变量,表示ArrayList中元素的个数。

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8683452581122892189L;
    // 默认容量为10的空Object数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // ArrayList中存储元素的数组
    transient Object[] elementData;
    // ArrayList中元素的个数
    private int size;
    // ...
}

ArrayList的构造方法

在创建ArrayList对象时,可以选择使用不同的构造方法来指定容量和初始元素。

ArrayList()构造方法

默认情况下,使用无参构造方法创建的ArrayList对象,容量为0,此时elementData指向一个空数组对象。

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

ArrayList(int initialCapacity)构造方法

如果需要指定ArrayList的容量大小,可以使用带参数的构造方法。在使用这个构造方法时,需要传入一个int类型的参数initialCapacity,表示ArrayList的初始容量。在实例化ArrayList对象时,elementData数组的长度将被初始化为initialCapacity。

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // 创建指定大小的Object数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // 创建空Object数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

ArrayList(Collection<? extends E> c)构造方法

除此之外,还可以使用带参数的构造方法,将一个已有的Collection集合转化为ArrayList。

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    size = elementData.length;
    // 如果elementData不是Object类型的数组,则将其转换为Object类型的数组
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, size, Object[].class);
}

ArrayList的扩容机制

当向ArrayList中添加元素时,如果当前ArrayList的容量不足,就需要对ArrayList进行扩容。ArrayList的扩容机制是在底层数组elementData不足以存储所有元素时,将其扩容为原来的1.5倍

private void grow(int minCapacity) {
    // oldCapacity是旧数组的长度
    int oldCapacity = elementData.length;
    // newCapacity是新数组的长度,为旧数组长度的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果新数组的长度小于minCapacity,则将minCapacity作为新数组的长度
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 如果新数组的长度大于MAX_ARRAY_SIZE,则将新数组的长度设置为Integer.MAX_VALUE - 8
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 将旧数组的元素复制到新数组中
    elementData = Arrays.copyOf(elementData, newCapacity);
}

其中,MAX_ARRAY_SIZE是一个静态final变量,用来限制数组的最大长度。如果扩容后的数组长度超过MAX_ARRAY_SIZE,则会抛出OutOfMemoryError异常。

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

ArrayList的添加元素方法

当向ArrayList中添加元素时,需要使用add方法。add方法有两个重载版本:

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++;
}

ensureCapacityInternal方法

在使用add方法添加元素之前,需要调用ensureCapacityInternal方法来确保ArrayList的容量足够存储新元素。如果ArrayList的容量不足,则会自动进行扩容。

private void ensureCapacityInternal(int minCapacity) {
    // 判断当前数组长度是否小于minCapacity
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

ensureExplicitCapacity方法

在ensureCapacityInternal方法中,会调用ensureExplicitCapacity方法,用于确保ArrayList的容量足够存储新元素。

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // 如果需要扩容,则进行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

add方法

在调用ensureCapacityInternal方法之后,add方法会将新元素添加到ArrayList中。如果是使用第二个重载版本的add方法,还需要将指定位置之后的元素后移一位。

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++;
}

其中,rangeCheckForAdd方法用于检查指定位置是否超出ArrayList的范围。

private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

ArrayList的删除元素方法

当从ArrayList中删除元素时,需要使用remove方法。remove方法有两个重载版本:

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

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;
}

fastRemove方法

在remove方法的第一个重载版本中,会调用fastRemove方法来删除元素。

private void fastRemove(int index) {
    modCount++;
    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
}

rangeCheck方法

在remove方法的第二个重载版本中,会调用rangeCheck方法来检查指定位置是否超出ArrayList的范围。

private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

ArrayList的迭代器

当需要遍历ArrayList中的元素时,可以使用迭代器。ArrayList的迭代器实现了Iterator接口,提供了hasNext、next、remove方法,可以在遍历ArrayList时进行删除操作。

public class Itr implements Iterator<E> {
    int cursor;       // 下一个元素的索引
    int lastRet = -1; // 上一个元素的索引
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings
    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];
    }
    
    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();
    
        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
    
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

在迭代器的实现中,cursor表示下一个元素的索引,lastRet表示上一个元素的索引。checkForComodification方法用于检查迭代器是否遍历过程中被修改过。在调用迭代器的remove方法时,会调用ArrayList的remove方法来删除元素,并更新迭代器的状态。

总结

本文通过对ArrayList的源码分析,介绍了ArrayList的实现原理和常用方法的实现细节。ArrayList是Java中常用的集合类之一,其底层实现使用数组,支持快速随机访问和动态扩容,具有较高的性能。了解ArrayList的底层源码,可以帮助开发人员更好地理解Java集合类的实现原理,为实现高效的Java应用程序提供基础支持。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员雨哲AI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值