ArrayList源码分析

本文深入解析了ArrayList的源码,包括其继承实现、成员变量、构造方法、增删改查操作。重点介绍了ArrayList的扩容机制、数组拷贝以及在添加、删除元素时的性能考量。此外,还探讨了ArrayList的线程安全性问题。

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

ArrayList概述

本文讲述的是jdk1.8的ArrayList。ArrayList位于jdk的rt.jar中,java.util.ArrayList。

一个类中有以下内容:
继承、实现的类:说明了该类继承哪些特性。
属性:分为公有、私有、受保护的、默认。
方法:分为构造方法、其他方法,同样也分为公有、私有、受保护的、默认。
内部类:顾名思义,类里面的类,为何放在类里面呢,就是自己用呗。

我们分析ArrayList的源码,也是从类中的内容下手。

ArrayList的继承、实现关系

首先看ArrayList的继承、实现关系。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
}

java.util.ArrayList继承java.util.AbstractList,实现java.util.List、java.util.RandomAccess、java.lang.Cloneable、java.io.Seriializable。

  • java.util.AbstractList:它实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。一般这种抽象类中都会有部分的默认实现,ArrayList继承AbstractList以后,会继承部分功能,不需要实现。
  • java.util.List:List接口,定义了数组队列的一些增删改查方法。其实ArrayList继承的AbstractList类已经实现了List接口,再次实现可能是便于阅读代码,历史版本迭代失误了。
  • java.util.RandomAccess:标志接口,有点像现在的注解。接口内容空的,实现它的类具体“快速随机访问功能”,也就是ArrayList中的get方法。详情见我的博客《ArrayList源码之6260652 bug号》。
  • java.lang.Cloneable:标志接口,实现该接口,就实现克隆功能,下面源码会介绍。
  • java.io.Seriializable:标志接口,实现该接口,该对象就可以被序列化,用于网络传输或持久化存储。那网络传输完成或持久化完成后,就需要反序列化。

ArrayList中声明的变量、常量

为了便于下面讲解源码,对ArrayList中声明的属性做一个简单的介绍。为了防止误导别人,英文注释任然保留。

 /**
     * 序列化编号,因为实现类java.io.Serializable,ArrayList可以被序列化。
     * 序列化与反序列化就用serialVersionUID来验证版本的一致性
     */
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     * ArrayList默认初始化大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     * 空数组实例,如果集合的大小为0,elementData就赋值为DEFAULT_CAPACITY
     * 区别于DEFAULTCAPACITY_EMPTY_ELEMENTDATA,DEFAULTCAPACITY_EMPTY_ELEMENTDATA是默认构造方法时,数组的默认值
     */
    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.
     * 空数组实例,ArrayList默认构造方法中,不指定集合大小时,数组的默认值。
     */
    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修饰表示该属性不参与序列化
     * non-private to simplify nested class acces这段注释,有空研究
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     * 集合的大小
     * 这个要区别于elementData.length。
     * elementData.length表示的是集合有这么大地方,但可能没存满。
     * size是集合中真正存储了多少个元素
     */
    private int size;

ArrayList中的方法总结

构造方法
  • public ArrayList()
  • public ArrayList(Collection<? extends E> c)
  • public ArrayList(int initialCapacity)
public、protected方法

public的方法,供ArrayList实例调用的方法,例如add、remove、set、get等。

add方法
  • public boolean add(E e) :为集合在末尾添加指定元素e。
  • public void add(int index, E element) :为集合在指定位置index添加指定元素 element。
  • public boolean addAll(Collection<? extends E> c):为集合在末尾添加指定集合c中的元素。
  • public boolean addAll(int index, Collection<? extends E> c) :为集合在指定位置index添加指定集合c。
remove、retainAll方法
  • public E remove(int index):移除集合中指定位置index的元素。
  • public boolean remove(Object o) :移除集合中指定元素o。
  • public boolean removeAll(Collection<?> c):移除集合中指定集合中c中的元素。
  • public boolean retainAll(Collection<?> c):和removeAll正好相反,保留了参数集合c中有的元素。
  • public boolean removeIf(Predicate<? super E> filter):移除集合中符合filter过滤规则的元素。
  • protected void removeRange(int fromIndex, int toIndex):移除集合中fromIndex~toIndex位置的元素。
set、replaceAll
  • public E set(int index, E element):修改集合中指定位置index的元素element。
  • public void replaceAll(UnaryOperator operator):按照指定操作规则,替换集合中的元素。与removeIf类似,可以用内部类,也可以用lamda表达式。
get方法
  • public E get(int index):获取集合中指定位置index的元素。
clear方法
  • public void clear() :清空集合中的所有元素。
clone方法
  • public Object clone():克隆方法,克隆出一个新的ArrayList。
indexOf、lastIndexOf、contains、isEmpty、方法
  • public int indexOf(Object o):返回元素o在集合中的脚标,否匹配到该元素,用.equals方法,如果没有该元素,则返回-1。
  • public int lastIndexOf(Object o):区别于indexOf,lastIndexOf是从后往前遍历的,遇到元素则返回脚标。
  • public boolean contains(Object o):集合中是否包含元素o,巧妙的复用了indexOf方法。indexOf方法返回-1,则表示不包含该元素,就返回false,否则返回true。
  • public boolean isEmpty():集合是否为空,用size变量做的判断。
ensureCapacity方法
  • public void ensureCapacity(int
    minCapacity):扩容数组的方法,如果对集合大小有个期望,可以主动调用此方法,一次性将数组扩容到位,避免频繁扩容数组消耗性能。

ArrayList的构造方法

ArrayList有三个构造方法,如下文所示。分别是初始化指定大小的集合、初始化默认的集合、以指定集合为基础初始化一个集合。其实构造方法就是初始化集合中elementData数组,给这个数组赋值。

ArrayList(int initialCapacity)

源码如下:

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

从代码可以看到,初始化集合,就是初始化elementData 。elementData 数组是存储集合元素的数组,也就是说,集合元素都存储在elementData 中。

  • 构造方法参数集合初始化大小如果为负数,抛异常;
  • 如果大于0,初始化指定大小的数组;
  • 如果为0,给定默认空数组EMPTY_ELEMENTDATA;
ArrayList()

源码如下:

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

很简单,初始化一个空的数组。
同样是空数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA区别于EMPTY_ELEMENTDATA。

ArrayList(Collection<? extends E> c)

源码如下:

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

首先把构造方法传进来的集合转换为数组。

  • 如果数组的长度不等于0,且数组的类型是Objectp[].class,直接将集合转化的数组赋值给本集合的数组elementData ;
  • 如果数组的长度不等于0,且数组的类型不是Object[].class,将集合转化的数组转成Object[].class,这样就可以赋值给本集合的数组elementData。这么做的原因可参考我的博客《
    ArrayList源码之6260652 bug号》;
  • 如果数组的长度等于0,把空数组EMPTY_ELEMENTDATA赋值给本集合的数组elementData ;

ArrayList的增删改查方法

ArrayList其实就是个数据结构,既然是数据结构,他的功能就是存储数据、获取存储的数据、修改存储的数据,也就是下面将要介绍的add、get、delete、set方法。

add方法

add方法是向集合中添加元素,jdk1.8中有如下四个方法:

  • public boolean add(E e):向集合结尾追加元素e
  • public void add(int index, E element):向集合指定位置index追加元素e
  • public boolean addAll(Collection<? extends E> c):向结合结尾追加集合c
  • public boolean addAll(int index, Collection<? extends E>
    c):向集合指定位置index追加集合c
boolean add(E e)

该方法入参为将要添加到集合中的元素,返回结果为是否添加成功。
源码如下:

public boolean add(E e) {
        //我需要size + 1这么大的数组存储数据,如果不够大,得扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //将元素e添加到集合的结尾
        elementData[size++] = e;
        //返回添加成功标识true
        return true;
}

源码中的注释“// Increments modCount!!”参考博客文章《xxx》。
从源码中看到,表面只有三行代码。

  • 想要向ArrayList中添加元素,首先得看看ArrayList的elementData数组够不够大,如果不够大需要扩容,这是第一行代码。ensureCapacityInternal(size + 1);
  • 既然elementData数组地方够大了,可以存储下新添加的数据了,则把e元素存储在数组的末尾。这是第二行代码。elementData[size++] = e;
  • 新添加的元素存储成功后,返回添加成功的标识。这是第三行代码。 return true;

下面的方法的含义是,我需要minCapacity这么大的数组,不满足大小则需要扩容。

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

下面的代码含义是根据原数组元素及期望的最小数组长度,计算出期望的数组长度值。
计算的原则是,取默认数组长度10和期望最小数组长度的最大值。这样的意义是啥呢?下面会介绍。

/**
     * 根据原数组元素及期望数组长度,计算期望数组的长度
     * @param elementData 原集合中存储的所有元素
     * @param minCapacity 期望数组长度的最小值
     * @return 计算出的期望数组长度
     */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //默认无参数构造的ArrayList时
        //如果期望数组长度小于DEFAULT_CAPACITY=10,则期望数组长度为DEFAULT_CAPACITY=10
        //如果期望数组长度大于DEFAULT_CAPACITY=10,则期望数组长度为minCapacity
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果结合中存储的元素数组为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,也就是new ArrayList()构造方法进来时
            //DEFAULT_CAPACITY为数组默认长度,值为10
            //期望数组长度大小为DEFAULT_CAPACITY与minCapacity的最大值
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //如果不是默认无参数构造的ArrayList时,期望数组长度为minCapacity
        return minCapacity;
    }

下面的代码是集合修改次数加一,扩容数组。

 /**
     * 集合修改的次数modCount+1
     * @param minCapacity
     */
    private void ensureExplicitCapacity(int minCapacity) {
        //集合修改的次数加一
        modCount++;
        // overflow-conscious code 上面的注释说明考虑到了溢出情况而设计的代码
        //
        if (minCapacity - elementData.length > 0)
            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
        //原来数组的长度
        int oldCapacity = elementData.length;
        //新数组的长度=1.5*原数组长度,>>1右移1位数学意义上就是除以2^1
        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);
    }

下面分析数组拷贝的方法。该方法定义在java.util.Arrays中

/**
     * Copies the specified array, truncating or padding with nulls (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>null</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     * The resulting array is of exactly the same class as the original array.
     *
     * @param <T> the class of the objects in the array。数组元素中的类型
     * @param original the array to be copied 将要被拷贝的数组
     * @param newLength the length of the copy to be returned 新数组的长度
     * @return a copy of the original array, truncated or padded with nulls
     *     to obtain the specified length。拷贝后的新数组
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException if <tt>original</tt> is null
     * @since 1.6
     */
 public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

最后用到了下面的代码,这个方法是native方法,用c写的,只看入参

  • scr:原始数组
  • srcPos:原始数组的脚标
  • dest:目标数组
  • destPos:目标数组的脚标
  • length:拷贝数组长度
    从原始数组src的第srcPos开始拷贝,拷贝length个元素到dest数组中,dest数组元素从第destPos开始放
public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

总结:

  1. 默认无参构造实例的对象elementData是空数。
  2. 向集合中添加元素时,先计算出期望的最小数组长度minCapacity,minCapacity这个值最起码保证elementData能足够大来存储所有的集合元素。
  3. 向ArrayList中添加元素时,可能是一个个向里面添加,总不能一次次扩容吧,扩容数组是数组拷贝的过程,消耗性能。所以elementData刚实例化是空数组,长度为0,添加一个元素后,数组的期望长度为
    DEFAULT_CAPACITY=10,根据期望长度,数组扩容到1.5*10=15
  4. 继续向集合中添加元素,如果集合元素大于DEFAULT_CAPACITY,期望数组长度变为minCapacity,数组扩容到1.5*minCapacity
  5. 继续添加元素,集合总得有个界限吧,集合的界限就体现在elementData的大小。elementData的最大长度MAX_ARRAY_SIZE=Integer.MAX_VALUE - 8,因为部分虚拟机数组对象的头上是有一些信息的,所以-8。可是有的时候还要继续向ArrayList中添加元素,所以不得已数组长度取值Integer.MAX_VALUE。
  6. 到头了,不能再扩了。
public void add(int index, E element)

该方法是向ArrayList的指定位置添加元素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).
     *向集合中的指定位置index添加元素element
     * @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 index, E element) {
         //检查index是否越界,越界抛异常
        rangeCheckForAdd(index);
        //参考add(E element)方法。确保数组足够大,不够大则扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //index前面的数组元素不需要变动,index及其后面的元素往后挪一个位置。
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        //将添加的元素放到指定位置
        elementData[index] = element;
        //集合大小加一
        size++;
    }
public boolean addAll(Collection<? extends E> c)

向ArrayList中添加集合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.)
     *将集合中的所有元素添加到ArrayList的末尾
     * @param c 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) {
        //将传进来的集合转为数组
        Object[] a = c.toArray();
        int numNew = a.length;
        //查看elementData是否满足期望值大小,不满足则扩容
        ensureCapacityInternal(size + numNew);  // Increments modCount
        //把集合c中的元素从第0个开始拷贝到elementData中,且从elementData的末尾开始放,拷贝c中的所有元素
        System.arraycopy(a, 0, elementData, size, numNew);
        //集合大小赋值
        size += numNew;
        //numNew!=0,说明c中有元素,有数据被成功添加到集合中
        return numNew != 0;
    }
public boolean addAll(int index, Collection<? extends E> c)

向集合指定index位置添加集合c,具体逻辑见源码中注释。

 {
        //检查脚标index是否越界,越界则抛异常
        rangeCheckForAdd(index);
        //将集合转为数组
        Object[] a = c.toArray();
        //参数集合c的大小
        int numNew = a.length;
        //检查elementData是否足够大,不够大则扩容
        ensureCapacityInternal(size + numNew);  // Increments modCount
        /**
         * 原集合的大小-新集合将要加入的位置=需要往后挪的元素的个数numMoved
         * elementData数组中index前面的元素保持不变,index位置的元素及后面的元素向后挪numNew个位置
         */
        int numMoved = size - index;
        if (numMoved > 0)
            //elementData数组中index前面的元素保持不变,index位置的元素及后面的元素向后挪numNew个位置
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);
        //将新集合c中的元素拷贝到elementData的index~numNew位置
        System.arraycopy(a, 0, elementData, index, numNew);
        //集合大小赋值
        size += numNew;
        //numNew!=0,说明c中有元素,有数据被成功添加到集合中
        return numNew != 0;
    }
remove方法

remove方法是将集合中的元素移除的方法。

public E 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 E remove(int index) {
        //检查index是否越界,越界则抛异常
        rangeCheck(index);
        //集合的修改次数加1
        modCount++;
        //将要被移除掉的元素
        E oldValue = elementData(index);
        //需要挪动元素的个数
        int numMoved = size - index - 1;
        if (numMoved > 0)
            //将集合中index位置后面的元素向左移动一位
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将集合中指定位置的元素置null,让GC工作
        elementData[--size] = null; // clear to let GC do its work
        //返回将要被移除掉的元素的值
        return oldValue;
    }
  • rangeCheck(int index)方法说明
/**
     * 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.
     * 检测脚标是否越界,越界则抛异常
     */
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
public boolean remove(Object o)

移除集合中的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).
     *移除集合中的指定元素。如何判断是否为指定的元素呢,用.equals方法,所以记得重写对象的.equals方法
     * @param o 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
            //遍历elementData数组,遇到为null的元素,则移除,而且直接返回
            //意味着只能移除遇到的第一个为null的元素
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    /*
                     * Private remove method that skips bounds checking and does not
                     * return the value removed.
                     * 私有的移除元素方法,不用校验index是否越界,不需要返回被移除的元素值。
                     * 除了这两条,和remove(int index)方法一样
                     */
                    fastRemove(index);
                    return true;
                }
        } else {
            //遍历elementData数组,遇到为o的元素,则移除,而且直接返回
            //意味着只能移除遇到的第一个为null的元素
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    /*
                     * Private remove method that skips bounds checking and does not
                     * return the value removed.
                     * 私有的移除元素方法,不用校验index是否越界,不需要返回被移除的元素值。
                     * 除了这两条,和remove(int index)方法一样
                     */
                    fastRemove(index);
                    return true;
                }
        }
        //没找到将要被移除的元素,也就是移除失败了呗,就返回false
        return false;
    }

  • fastRemove(index)方法说明
/*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     * 私有的移除元素方法,不用校验index是否越界,不需要返回被移除的元素值。
     * 除了这两条,和remove(int index)方法一样
     */
    private void fastRemove(int index) {
        //集合修改的次数加一
        modCount++;
        //将要被挪动的元素个数
        int numMoved = size - index - 1;
        if (numMoved > 0)
            //将集合中index位置后面的元素向左移动一位
            System.arraycopy(elementData, index+1, elementData, index,
         //返回将要被移除掉的元素的值                    numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }
public boolean removeAll(Collection<?> c)

移除ArrayList集合中,c集合中的元素,说明见源码:

public boolean removeAll(Collection<?> c) {
        //判断集合c是否为空,为空则抛异常。平时可以考虑使用的工具类
        Objects.requireNonNull(c);
        //如果complement为true,保留c中不存在的元素;如果complement为false,保留c中存在的元素
        return batchRemove(c, false);
    }
  • batchRemove(Collection<?> c, boolean complement)方法说明
 /**
     * 如果complement为true,保留c中不存在的元素;如果complement为false,保留c中存在的元素
     * @param c
     * @param complement
     * @return
     */
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        //原数组的脚标计数r、新数组的脚标计数w
        int r = 0, w = 0;
        //是否被修改,也就是是否有元素被真正移除
        boolean modified = false;
        try {
            /**
             * 遍历ArrayList中的元素,即elementData中的元素
             * 如果参数集合c中“存在该元素/不存在该元素”(与complement参数有关),则将该值放到elementData数组中,也就是保留在集合中
             */
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            /**
             * r!=size,意味着遍历elementData的过程中抛出异常,意外中断。
             * 所以怎么办呢?需要将剩下的没遍历的元素拷贝进去,进哪里呢,其实就是继续保留在ArrayList中
             */
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            /**
             * w!=size 意味着啥呢?意味着遍历elementData数组处理数据的过程中,有元素没有被保留,也就是相当于被删除了
             * 那elementData数组中就该有空位置,就该被置空,让GC回收
             * modCount怎么计数的呢,删一个元素+1,加一个元素+1,加两个元素+2
             */
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                //有元素被删除了,也就是被修改了
                modified = true;
            }
        }
        return modified;
    }
protected void removeRange(int fromIndex, int toIndex)

除集合中fromIndex~toIndex 的元素,具体逻辑见源码和注释

/**
     * Removes from this list all of the elements whose index is between
     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
     * Shifts any succeeding elements to the left (reduces their index).
     * This call shortens the list by {@code (toIndex - fromIndex)} elements.
     * (If {@code toIndex==fromIndex}, this operation has no effect.)
     *移除集合中fromIndex~toIndex 的元素
     * @throws IndexOutOfBoundsException if {@code fromIndex} or
     *         {@code toIndex} is out of range
     *         ({@code fromIndex < 0 ||
     *          fromIndex >= size() ||
     *          toIndex > size() ||
     *          toIndex < fromIndex})
     */
    protected void removeRange(int fromIndex, int toIndex) {
        //修改次数+1
        modCount++;
        //需要挪动的元素的个数
        int numMoved = size - toIndex;
        //把toIndex~toIndex+numMoved的元素向左移动numMoved个位置
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        //新集合的大小
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            //空的位置置空,方便GC
            elementData[i] = null;
        }
        size = newSize;
    }
public boolean removeIf(Predicate<? super E> filter)

按filter的过滤规则,移除掉集合中符合filter规则的元素,具体逻辑见源码及备注

/**
     * 按filter的过滤规则,移除掉集合中符合filter规则的元素
     * @param filter 移除元素满足的规则
     * @return 是否有元素移除成功
     */
    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        //判断规则参数是否为空,为空则抛出异常
        Objects.requireNonNull(filter);
        // figure out which elements are to be removed
        // any exception thrown from the filter predicate at this stage
        // will leave the collection unmodified
        //声明移除掉的元素个数,默认为0
        int removeCount = 0;
        //移除掉的元素的脚标,位图需要学习
        final BitSet removeSet = new BitSet(size);
        //声明期望的集合修改次数,为final
        final int expectedModCount = modCount;
        //声明集合的大小,size为final
        final int size = this.size;
        //modCount == expectedModCount这句话是检测并发情况的
        /**
         * 该段for循环的意义是如果符合过滤规则,则将脚标存入到removeSet中,并且removeCount计数加一
         */
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            @SuppressWarnings("unchecked")
            final E element = (E) elementData[i];
            if (filter.test(element)) {
                removeSet.set(i);
                removeCount++;
            }
        }
        //检测并发情况
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }

        // shift surviving elements left over the spaces left by removed elements
        //是否有元素需要移除
        final boolean anyToRemove = removeCount > 0;
        if (anyToRemove) {
            //新集合的大小
            final int newSize = size - removeCount;
            /**
             * 给新集合的元素赋值
             */
            for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
                i = removeSet.nextClearBit(i);
                elementData[j] = elementData[i];
            }
            //elementData中没有元素的位置置空,触发GC
            for (int k=newSize; k < size; k++) {
                elementData[k] = null;  // Let gc do its work
            }
            //集合大小赋值
            this.size = newSize;
            //检测并发情况,有则抛异常
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
            //集合修改次数加一
            modCount++;
        }
        //是否有元素移除成功
        return anyToRemove;
    }
set方法

set方法就是修改集合中的元素

public E 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 E set(int index, E element) {
        //检测index是否越界,越界则抛异常
        rangeCheck(index);
        //将要被修改的值
        E oldValue = elementData(index);
        //修改值
        elementData[index] = element;
        返回旧值
        return oldValue;
    }
get 方法

获取元素,也就是查

public E 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 E get(int index) {
        //检测脚标是否越界,越界则抛异常
        rangeCheck(index);
        //获取指定位置的元素
        return elementData(index);
    }
ArrayList简单总结
  • ArrayList存储数据,底层是数组。
  • ArrayList插入数据时需要扩容数组,因此尽量初始化数组的大小。
  • 添加、删除数据时需要移动数组中的元素,因此性能较差。
  • 获取数据时,可以根据数组脚标直接找到数据,因此较快。
  • ArrayList集合是有序的,因为底层是数组。
  • ArrayList允许插入空值,元素可以重复。
  • ArrayList线程不安全。
  • List list= Collections.synchronizedList(new ArrayList<>());会让ArrayList变成线程安全的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值