JAVA集合源码解析——ArrayList

本文深入解析ArrayList的底层实现原理,包括其数据结构、构造方法、核心操作如添加、删除、查询等方法的具体实现细节。

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

1.数据结构

底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对ArrayList类的实例的所有的操作底层都是基于数组的。

2.ArrayList中的全局变量

public class ArrayList<Eextends AbstractList<E>
        implements List<E>RandomAccessCloneablejava.io.Serializable {

    /**
     * 版本号
     */
    private static final long serialVersionUID 8683452581122892189L;

    /**
     * Default initial capacity.
     * 默认初始容量为10
     */
    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.
     * ArrayList底层的元素数组
     */
    transient Object[] elementData// non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     * ArrayList实际元素大小,默认为0
     */
    private int size;

    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     * 数组的最大容量
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE 8;
}

3.构造方法(初始化elementData)

(1).构造一个初始容量为10的空列表
/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    //将缺省的空对象数组赋给ArrayList底层的元素数组(默认数组容量为10后面会解释什么时候赋值的)
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

(2) .构造具有指定初始容量的空列表
/**
 * 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) {//将参数中容量的大小作为初始化elementData的大小
        this.elementData = new Object[initialCapacity];
   else if (initialCapacity == 0) {//如果为0将空对象数组赋给elementData
        this.elementData = EMPTY_ELEMENTDATA;
   else {//容量大小小于0抛出异常
        throw new IllegalArgumentException("Illegal Capacity: "+
                initialCapacity);
    }
}

(3) .构造包含指定元素的列表
/**
 * Constructs a list containing the elements of the specified
 * collection, in the order they are returned by the collection's
 * iterator.
 *
 * @param 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();
    //将数组长度赋值给size,并且判断数组长度不为0
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            //如果c.toArray()的类型是不是Object[].class类型则需要调用Arrays.copyOf方法进行转换一下
            elementData = Arrays.copyOf(elementDatasizeObject[].class);
   else {
        // replace with empty array.
        //如果数组长度是为0就将空对象数组赋给elementData
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

4.ArrayList中核心方法

(1) . add(E  e);
/**
 * Appends the specified element to the end of this list.
 * 将指定的元素追加到列表的末尾
 *
 * @param element to be appended to this list  要追加到这个列表的元素
 * @return <tt>true</tt> (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    //确定内部容量是否足够,size+1表示该数组添加一个单位后的长度
    ensureCapacityInternal(size + 1) // Increments modCount!!
    //在数组后一位(size++)中添加需要的元素,并且size++
    elementData[size++] = e;
    return true;
}

/**
 * 确定内部容量是否足够
 *
 * @param minCapacity minCapacity=size+1 即添加一个元素后的数组长度
 */
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //如果初始数组为空数组,将minCapacity赋为默认大小10(注:这里还没初始化elementData这个数组大小为10)
        minCapacity = Math.max(DEFAULT_CAPACITYminCapacity);
    }
    //判断elementData数组长度是否够用
    ensureExplicitCapacity(minCapacity);
}

/**
 * 判断elementData数组长度是否够用
 *
 * @param minCapacity 用来判断数组长度是否满足此参数
 */
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    //判断minCapacity与elementData数组长度
    if (minCapacity - elementData.length > 0)
        //如果minCapacity大于elementData数组长度,则说明elementData数组长度不够,需要调用grow方法进行扩容操作
        grow(minCapacity);
}

/**
 * Increases the capacity to ensure that it can hold at least the
 * number of elements specified by the minimum capacity argument.
 * 增加容量以确保它至少能容纳由最小容量参数指定的元素数量。
 *
 * @param minCapacity the desired minimum capacity  期望扩容至的最小容量
 */
private void grow(int minCapacity) {
    // overflow-conscious code
    //定义oldCapacity为扩容之前的数组大小
    int oldCapacity = elementData.length;
    //定义newCapacity的值为oldCapacity*1.5
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)//这步判断elementData为空数组的时候oldCapacity=0,newCapacity=0,minCapacity=10该条件成立,将minCapacity赋值给elementData需要扩容的大小
        //这里解释了为什么无参构造函数数组的默认大小为10
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)//判断newCapacity和最大数组容量的大小
        //如果需要扩容的大小超出最大数组长度调用hugeCapacity将最大值赋给newCapacity
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    //调用Arrays中的copyOf方法copy旧数组改变容量大小
    elementData = Arrays.copyOf(elementDatanewCapacity);
}

/**
 * 处理需要扩容的大小超出最大数组长度
 *
 * @param minCapacity 期望扩容至的最小容量
 * @return 实际扩容的大小
 */
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0// overflow 抛出异常
        throw new OutOfMemoryError();
    //如果minCapacity(期望扩容至的最小容量) < MAX_ARRAY_SIZE(数组最大容量)则返回MAX_ARRAY_SIZE,反之返回Integer.MAX_VALUE(0x7fffffff)
    return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
}

(2). add(int  index,  E  element);
/**
 * Inserts the specified element at the specified position in this
 * list. Shifts the element currently at that position (if any) and
 * any subsequent elements to the right (adds one to their indices).
 * 在该列表中的指定位置插入指定元素。将当前在该位置的元素(如果有的话)和任何后续元素向右移动(将一个元素添加到它们的索引中)。
 *
 * @param index   index at which the specified element is to be inserted 要插入指定元素的索引
 * @param element element to be inserted 要插入的元素
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public void add(int indexE element) {
    //插入元素索引校验
    rangeCheckForAdd(index);
    //确定内部容量是否足够,size+1表示该数组添加一个单位后的长度(与上面说的add(E e)中的ensureCapacityInternal一样)
    ensureCapacityInternal(size + 1) // Increments modCount!!
    //将index及其后边的所有的元素整块后移,空出index位置
    System.arraycopy(elementDataindexelementDataindex + 1,
            size - index);
    //插入指定的元素
    elementData[index] = element;
    //该数组元素大小++
    size++;
}

/**
 * A version of rangeCheck used by add and addAll.
 * 插入元素索引校验
 */
private void rangeCheckForAdd(int index) {
    //插入元素的位置不能大于该数组的大小并且不能小于0
    if (index > size || index < 0)
        //抛出该索引的数组越界异常
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

(3). addAll(Collection<?   extends  E>  c);
/**
 * Appends all of the elements in the specified collection to the end of
 * this list, in the order that they are returned by the
 * specified collection's Iterator.  The behavior of this operation is
 * undefined if the specified collection is modified while the operation
 * is in progress.  (This implies that the behavior of this call is
 * undefined if the specified collection is this list, and this
 * list is nonempty.)
 * 以指定集合的迭代器返回的顺序将指定集合中的所有元素追加到列表的末尾
 *
 * @param collection containing elements to be added to this list  需要添加此列表的集合元素
 * @return <tt>true</tt> if this list changed as a result of the call
 * @throws NullPointerException if the specified collection is null
 */
public boolean addAll(Collection<? extends E> c) {
    //将c集合转化为对象数组a
    Object[] a = c.toArray();
    //numNew为a数组对象的长度
    int numNew = a.length;
    //确定内部容量是否足够,size+numNew表示该数组添加a数组对象后的长度(与上面说的add(E e)中的ensureCapacityInternal一样)
    ensureCapacityInternal(size + numNew) // Increments modCount
    //将对象数组a拷贝到elementData中去
    System.arraycopy(a0elementDatasizenumNew);
    //添加c集合后修改该数组元素的长度
    size += numNew;
    //如果加入的集合为空返回false
    return numNew != 0;
}

(4). remove(int  index);
/**
 * Removes the element at the specified position in this list.
 * Shifts any subsequent elements to the left (subtracts one from their
 * indices).
 * 删除该列表中指定位置的元素。
 *
 * @param index the index of the element to be removed 要删除元素的索引
 * @return the element that was removed from the list  返回从列表中移除的元素
 * @throws IndexOutOfBoundsException {@inheritDoc}    抛出数组越界异常
 */
public remove(int index) {
    //要删除元素的索引校验
    rangeCheck(index);
    modCount++;
    //通过索引可以直接找到要删除的元素
    E oldValue = elementData(index);
    //计算总共要移动元素的个数为下面System.arraycopy方法中的参数做准备
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementDataindex + 1elementDataindex,
                numMoved);
    //将--size上的位置赋值为null,让gc(垃圾回收机制)更快的回收它。
    elementData[--size] = null; // clear to let GC do its work
    //返回删除的元素
    return oldValue;
}

(5). remove(Object  o);
/**
 * Removes the first occurrence of the specified element from this list,
 * if it is present.  If the list does not contain the element, it is
 * unchanged.  More formally, removes the element with the lowest index
 * <tt>i</tt> such that
 * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
 * (if such an element exists).  Returns <tt>true</tt> if this list
 * contained the specified element (or equivalently, if this list
 * changed as a result of the call).
 * 移除列表中第一次出现该指定元素的索引元素
 *
 * @param element to be removed from this list, if present
 * @return <tt>true</tt> if this list contained the specified element
 */
public boolean remove(Object o) {
    if (o == null) {//如果删除的元素为null
        for (int index = 0index < sizeindex++)//遍历elementData
            if (elementData[index] == null) {
                //如果elementData[index] == null调用fastRemove()方法移除元素
                fastRemove(index);
                //跳出遍历  返回true
                return true;
            }
    } else {
        for (int index = 0index < sizeindex++)//遍历elementData
            if (o.equals(elementData[index])) {
                //如果指定删除的元素和elementData[index]相等调用fastRemove()方法移除元素
                fastRemove(index);
                //跳出遍历  返回true
                return true;
            }
    }
    //如果elementData中没有找到指定要删除的元素o则返回false
    return false;
}

/**
 * Private remove method that skips bounds checking and does not
 * return the value removed.
 * 跳过检查的私有删方法(与E remove(int index);的删除方法一致)
 */
private void fastRemove(int index) {
    modCount++;
    //计算总共要移动元素的个数为下面System.arraycopy方法中的参数做准备
    int numMoved = size - index - 1;
    if (numMoved > 0)//如果需要移动的元素大于0
        //调用System.arraycopy()方法移动元素
        System.arraycopy(elementDataindex+1elementDataindex,
                numMoved);
    //将--size上的位置赋值为null,让gc(垃圾回收机制)更快的回收它。
    elementData[--size] = null; // clear to let GC do its work
}

(6). set(int  index,  E  element);
/**
 * Replaces the element at the specified position in this list with
 * the specified element.
 * 用指定的元素替代指定位置上的元素
 *
 * @param index   index of the element to replace 替换元素的索引
 * @param element element to be stored at the specified position 要在指定位置存储的元素
 * @return the element previously at the specified position 返回替换之前指定位置上的元素
 * @throws IndexOutOfBoundsException {@inheritDoc}  抛出数组越界异常
 */
public set(int indexE element) {
    //索引检验
    rangeCheck(index);
    //根据索引获得旧值oldValue
    E oldValue = elementData(index);
    //根据索引赋予新值element
    elementData[index] = element;
    //返回旧值
    return oldValue;
}

/**
 * Checks if the given index is in range.  If not, throws an appropriate
 * runtime exception.  This method does *not* check if the index is
 * negative: It is always used immediately prior to an array access,
 * which throws an ArrayIndexOutOfBoundsException if index is negative.
 * 检查给定索引是否在范围内。如果没有,则抛出一个适当的运行时异常。此方法不*检查索引是否为负值:
 * 它总是在数组访问之前立即使用,如果索引为负,则抛出ARRayIndex OutOfFunsExchange异常。
 */
private void rangeCheck(int index) {
    if (index >= size)//如果索引大于该数组长度size,抛出数组越界异常
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

(7). get(int  index);
/**
 * Returns the element at the specified position in this list.
 * 返回列表中指定位置的元素
 *
 * @param index index of the element to return 返回元素的索引
 * @return the element at the specified position in this list 列表中指定位置的元素
 * @throws IndexOutOfBoundsException {@inheritDoc} 抛出数组越界异常
 */
public get(int index) {
    //索引检验(上面讲过,不多解释)
    rangeCheck(index);
    //返回列表中指定位置的元素
    return elementData(index);
}

(8). indexOf(Object  o);
/**
 * Returns the index of the first occurrence of the specified element
 * in this list, or -1 if this list does not contain the element.
 * 返回此列表中指定元素的第一次出现的索引,或如果该列表不包含元素返回-1。
 * <p>
 * More formally, returns the lowest index <tt>i</tt> such that
 * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
 * or -1 if there is no such index.
 */
public int indexOf(Object o) {
    if (o == null) {//如果该指定元素为null
        for (int i = 0i < sizei++)//遍历elementData
            if (elementData[i] == null)
                //如果elementData[index] == null返回该elementData索引
                return i;
   else {//指定元素不为null
        for (int i = 0i < sizei++)//遍历
            if (o.equals(elementData[i]))
                //如果指定元素与elementData[i]值相同则返回此时elementData索引
                return i;
    }
    //如果elementData中不存在该指定元素时返回-1
    return -1;
}

5.ArrayList总结

(1).ArrayList基于数组方式实现,他与数组的区别在于能够自动扩容,其中扩容的核心方法为grow()。
(2).添加元素时可能会触发扩容的操作,扩容会复制数组操作速率比较差(所以在初始化的时候最好预判一下)。
(3).remove(Object o)需要遍历数组,并且移动元素效率比较差;而remove(int index)不需要遍历数组只需要效率比remove(Object o)稍高。
(4).get(int index)和set(int index, E element); 可以通过索引直接获取或者操作效率高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值