Java集合框架分析(List)——ArrayList类详解

ArrayList类中的方法与实现原理

目录

  1. public ArrayList(int initialCapacity)
  2. public ArrayList()
  3. public ArrayList(Collection c)
  1. public boolean add(Object o)
  2. public void add(int index,Object element)
  3. .public boolean addAll(Collection c)
  4. public boolean addAll(int index,Collection c)
  5. public void clear()
  6. public Object clone()
  7. .public boolean contains(Object elem)
  8. public boolean containsAll(Collection c)
  9. public boolean equals(Object o)
  10. public Object get(int index)
  11. public int hashCode()
  12. public int indexOf(Object elem)
  13. public boolean isEmpty()
  14. public Iterator iterator()
  15. public int lastindexOf(Object elem)
  16. public ListIterator listIterator()
  17. public ListIterator listIterator(final int index)
  18. public boolean remove(Object o)
  19. public Object remove(int index)
  20. public boolean removeAll(Collection c)
  21. public boolean retainAll(Collection c)
  22. public Object set(int index,Object element)
  23. public int size()
  24. public List subList(int fromIndex,int toIndex)
  25. public Object[] toArray()
  26. public Object[] toArray(Object []a)
  27. public String toString()
  28. public void trimToSize()

一.数据结构

ArrayList的底层数据结构就是一个数组,数组元素的类型为Object类型,对ArrayList的所有操作底层都是就与数组的。
对ArrayList进行添加元素的操作的时候是分两个步骤进行的,即第一步先将Object[size]的位置上存放需要添加的元素;第二步将size的值加1。由于这个过程在多线程的环境下是不能保证具有原子性的,因此ArrayList在多线程的环境下是线程不安全的。
如果非要在多线程的环境下使用ArrayList,就需要保证它的线程安全性,通常有两种解决办法:第一,使用synchronized关键字;第二,可以用Collections类中的静态方法synchronizedList();对ArrayList进行调用。

二.类标题

ArrayList类的标题如下:

public class ArrayList extends AbstractList implements List,Cloneable,java.io.Serializable

这个标题说明ArrayList类是AbstractList类的子类,并且实现了三个接口:List、Cloneable和Serializable。如下图所示:
Java集合框架分析(List)——ArrayList类详解

[Q1]为什么ArrayList继承了AbstractList还要实现List接口

Serializable接口源码:

package java.io;


public interface Serializable
{
}

凡是实现Serializable接口(java.io)的类都可以进行序列化和反序列化操作

[Q2]:Serializable为什么要定义一个空接口来进行标识

[Q3]:如何理解: implements Cloneable表示该对象能被克隆,能使用Object.clone()方法。如果没有implements Cloneable的类调用Object.clone()方法就会抛出CloneNotSupportedException。


三.字段

private transient Object elementData[];
private int size;//数组中元素的个数
protected transient int modCount = 0; //继承自AbstractList

【注】:成员变量modCount记录着集合的修改次数,也就是每次add或者remove它的值都会加1


四.构造函数

1.public ArrayList(int initialCapacity);
作用:ArrayList类的构造方法,根据传入的initialCapacity值创建一个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);
        }
    }

源码解析:
传入initialCapacity为0,创建空数组,否则,创建intialCapacity大小的数组elementData

2.public ArrayList();

作用:ArrayList类的构造方法,创建一个空的数组。在第一次添加元素时容量扩大到10
源码如下:

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

源码分析:创建一个空数组

3.public ArrayList(Collection c);

作用:使用指定Collection来构造ArrayList的构造函数
源码如下:

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();//将传入的集合转换为一个数组,并将数组的应用赋给elementData
        if ((size = elementData.length) != 0) {//数组不为空
            // defend against c.toArray (incorrectly) not returning Object[]
            if (elementData.getClass() != Object[].class)//数组不为Object[],将其复制成为Object
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {//数组为空的情况
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

分析:
1.将传入的集合转换为数组
2.判断转换后的数组是否为空
若为空:创建一个空的elementData数组
若不为空,且转换后的数组类型不为Object[]类型,将其复制为符合Object[]数组类型的elementData数组


五.其他方法

1.public boolean add(Object o);
作用:将指定元素追加到列表的末尾。返回值是true
源码如下:
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  //判断是否需要扩容
        elementData[size++] = e;    //将元素插入到已有元素后一个位置
        return true;
    }
    
    //将预计插入元素后所需最小容量传入EnsureCapacityInternal
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果调用的无参构造方法(创建空数组),minCapacity=10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 如果传入的预计插入后的最小容量大于当前数组的容量,用grow进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    //对elementData[]进行扩容,每次增加1/2 elementData数组长度。
    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:
        // 调用 Arrays.copyOf 方法将 elementData 数组指向新的内存空间 newCapacity 的连续空间
         // 并将 elementData 的数据复制到新的内存空间
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

源码分析:
ArrayList集合add()过程如下图所示:

Java集合框架分析(List)——ArrayList类详解

[Q4]add(E e)方法为什么总是返回true?
不理解:[此方法总是返回true,是因为在Collection接口中对应的方法返回的是一个boolean值:如果调用对象所属类允许重复,或者被添加的元素尚不再调用对象中,就返回true,否则返回false.有些Collections接口的实现就不允许重复]

【注】Max_ARRAY_SIZE定义为private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE-8 其中,Integer.MAX_VALUE是Integer类中的一个int类型的常量MAX_VALUE,它代表int所能表示的最大值Ox7FFFFFFF

2.public void add(int index,Object element);

作用:将指定元素插入此列表的指定位置。将当前位置的元素(如果有)和任意后续元素向右移动。

源码如下:

//elementData数组在索引index处插入元素
public void add(int index, E element) {
    rangeCheckForAdd(index);//检查index索引是否越界
    ensureCapacityInternal(size + 1);  //扩容,源码在1.public boolean add(Object o)c处
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    elementData[index] = element;
    size++;
}

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

//java.lang.System
/*
    @Function:复制数组,以插入元素,但是要将index之后的元素都往后移一位。然后就是插入元素,增加sized的值
    @src:源数组  srcPos:源数组要复制的起始位置   dest:目的数组  destPos:目的数组放置的起始位置  length:复制的长度
*/
public static native void arraycopy(Object src,int srcPos,Object dest, int destPosint length);

源码分析:

1.边界检查。检查传入的index索引是否越界。

2.容量判断。判断数组是否有足够空间接收元素,不够的话进行扩容操作。

3.右移。对索引后的元素进行右移一位操作。

4.插入元素。将新元素插入到数组索引处。

5.记录数组元素个数的size+1.

System.arraycopy(elementData, index, elementData, index + 1, size - index);

复制源数组elementData从index处开始size-index长度(复制index后面的元素)到elementData数组的index+1位置。图示如下:
Java集合框架分析(List)——ArrayList类详解
[Q5]native关键字的使用

3.public boolean addAll(Collection c);

作用:将指定集合中的所有元素插入此列表末尾

源码如下:

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // 同4.public boolean add(Object o)分析,确保数组容量足够
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

源码分析:

1.将集合转换为数组。

2.确保加入新数组元素后,数组容量足够

3.将转换后的数组添加到已有数组末尾

4.记录数组元素个数的size+numNew

System.arraycopy(a, 0, elementData, size, numNew);

复制a数组从索引0-numNew的元素到elementData数组的size位置。源码及分析在2.public void add(int index,Object element)处;

4.public boolean addAll(int index,Collection c);

作用:从指定位置开始,将指定集合中的所有元素插入此列表。将当前位置的元素元素往后移。参数列表中两个参数,第一个参数,填写数组下标,第二个参数,直接填写要被插入的数组名即可。

源码如下:

 public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);//索引边界检查,源码在);

    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // 确保容量足够,源码在1.public boolean add(Object o);

    int numMoved = size - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);//源码在2.public void add(int index,Object element)

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

源码分析:

1.索引边界检查

2.将要插入的集合转换为数组

3.确保加入新数组元素后,数组容量足够

4.计算已有数组在index后的元素个数

5.将已有elementData数组index后的numMoved位元素右移numNew位

6.将常茹为numNew位的新数组插入到index的位置

7.记录elementData数组元素个数的size+numNew

5.public void clear();

作用:从此列表中删除所有元素。

源码如下:

public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

源码分析:
成员变量modCount记录集合的修改次数。clear()方法原理即将elementData数组的所有元素赋空值。

6.public Object clone();

作用:返回此ArrayList实例的浅表副本。若要赋值给一个对象,需要强制转型。
源码如下:

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

//Arrays.copyOf().Arrays的copyOf()方法传回的数组是新的数组对象,改变传回数组中的元素值,不会影响原来的数组。copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值
public static byte[] copyOf(byte[] original, int newLength) {//original 需要复制的源数组
    byte[] copy = new byte[newLength];
    System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
    return copy;
}

//from java.lang.Object
protected native Object clone() throws CloneNotSupportedException;

浅克隆:将新集合每个元素的引用指向原集合对应对象在栈空间的内存地址,所以,原集合修改或删除,克隆的集合随之改变;新集合修改添加会改变引用重新指向其他堆内存地址,删除就直接删除引用。

深克隆:将新集合的元素对象复制,在堆内存中重新开辟空间存一样的内容,一般要对集合中的对象重写clone(),在clone()中返回new的新对象,再add到新集合中,所以新旧集合操作互不影响。

源码分析:

ArrayList中的clone方法继承自Object类。ArrayList实现的clone()为浅克隆,即复制对象的引用。

1.利用Object类中的clone(),复制elementData数组的引用v

2.将已有数组elementData数组元素复制给v指向的elementData数组

[Q6]super.clone()进行了什么操作,v.elementData表示什么?

7.public boolean contains(Object elem);

作用:如果此列表中包含此元素,则返回true
源码如下:

public boolean contains(Object o) {
    return indexOf(o) >= 0;  //indexOf(Object o)方法同15.public int indexOf(Object elem);
}

源码分析:
查找elementData数组中是否包含o元素。
若包含,返回元素所在数组单元的索引值;
若不包含,返回-1.

8.public boolean containsAll(Collection c);

作用:方法用于检测 arraylist 是否包含指定集合中的所有元素。
源码如下:

public boolean containsAll(Collection<?> c) {
    for (Object e : c)
        if (!contains(e))//源码在7.public boolean contains(Object elem);
            return false;
    return true;
}

源码分析:
ArrayList类的containsAll(Collection c)继承自AbstractCollection类。
该方法用for-each遍历c集合,同时查看c集合中是否包含e元素。
若包含,则返回ture.否则返回false.

9.public boolean equals(Object o);

作用:这里调用的equals方法并不是Object类中的。ArrayList继承了AbstractList< E>,它的equals方法应该从这个抽象类中来的。发现该类确实重写了equals方法,就是使用迭代器遍历两个List每个元素是否相等。 那么ArrayList调用equals方法就是元素进行比较了。
源码如下:

//inheried from java.util.AbstractList
public boolean equals(Object o) {
    if (o == this)//如果o为调用此方法的对象,则返回true
        return true;
    if (!(o instanceof List))//若对象o不是List对象,则返回false
        return false;

    //定义两个List迭代器用来遍历元素
    ListIterator<E> e1 = listIterator();
    ListIterator<?> e2 = ((List<?>) o).listIterator();
    while (e1.hasNext() && e2.hasNext()) {//当两个迭代器均有下一个元素
        E o1 = e1.next();
        Object o2 = e2.next();
        if (!(o1==null ? o2==null : o1.equals(o2)))
            return false;
    }
    return !(e1.hasNext() || e2.hasNext());
}

源码分析:

    if (!(o1==null ? o2==null : o1.equals(o2)))
        return false;

若o1为空,返回o2 == null.则变为if(!(o2==null));此时,若o2不为null,则直接返回false.

若o1非空,返回o1.equals(o2).则变为if(!(o1.equals(o2)))【注】此处的equals()方法应为Object类中的equals()方法.若o1与o2不等,则返回false.

return !(e1.hasNext() || e2.hasNext());

若e1和e2都遍历完了,未返回false,则认定equals()返回true.

10.public Object get(int index);

作用:返回此列表中指定位置的元素
源码如下:

public E get(int index) {
    rangeCheck(index);//检查索引是否越界,源码在2.public void add(int index,Object element)处

    return elementData(index);
}

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

源码分析:
1.检查索引是否越界
2.返回elementData数组指定索引index位置的元素。

11.public int hashCode();

作用:求ArrayList的哈希值,这个方法是ArrayList的抽象父类AbstractList中的方法
源码如下:

//inherited from java.util.AbstractList
public int hashCode() {
    int hashCode = 1;
    for (E e : this)
        hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
    return hashCode;
}

源码分析:
在JAVA语言中,判断两个对象是否相等,一般有两种方法,一种是hashcode(),另一种是equals(),这两个方法在判断准确性和效率上有很大的区别。hashCode()方法和equal()方法的作用其实一样,在Java里都是用来对比两个对象是否相等一致,那么equal()既然已经能实现对比的功能了,为什么还要hashCode()呢?
因为重写的equal()里一般比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高,那么hashCode()既然效率这么高为什么还要equal()呢?
因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠。

12.public int indexOf(Object elem);

作用:返回此列表中元素第一次出现的位置下标,如果没有此元素则返回-1
源码如下:

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                 return i;
    }
    return -1;
}

源码分析:
ArrayList类中indexOf(Object elem)重写了AbstractList类中的方法。

1.判断传入元素是否为空

若为空,遍历数组元素,用“==”找出数组单元元素为空所在的索引

若不为空,遍历数组元素,用“equals()”找出数组中与传入元素内容相同的单元所在的索引

否则,返回-1。

13.public boolean isEmpty();

作用:判断此列表中是否为空,空则返回true,否则返回false
源码如下:

public boolean isEmpty() {
    return size == 0;
}

源码分析:
如果记录数组元素个数的size==0,表示数组中没有元素,所以返回true.否则,返回false.

14.public Iterator iterator();

作用:返回当前集合的双向迭代器
每个实现Iterable接口的类必须提供一个iterator方法,返回一个Iterator对象,ArrayList也不例外
源码如下:

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

返回的是一个Itr类的对象,接下来我们来看它的源码

private class Itr implements Iterator<E> {
    int cursor = 0;//指向下一个要被迭代的元素
    int lastRet = -1;//指向当前元素
    int expectedModCount = modCount;//在迭代器初始化过程中会将modCount赋给迭代器的expectedModCount。在迭代过程中,判断 modCount 跟 expectedModCount 是否相等,如果不相等就表示通过其他方法修改了 ArrayList的结构

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

    public E next() {//将cursor指向下一个被迭代的元素,lastRet指向当前元素
        checkForComodification();//检查是否有线程在修改数组结构
        try {
            int i = cursor;
            E next = get(i);
            lastRet = i;
            cursor = i + 1;
            return next;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            AbstractList.this.remove(lastRet);//删除lastRet指向的数组单元
            if (lastRet < cursor)
                cursor--;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException e) {
            throw new ConcurrentModificationException();
        }
    }
    //有两个线程(线程A,线程B),其中线程A负责遍历list、线程B修改list。
-线程A在遍历list过程的某个时候(此时expectedModCount = modCount=N),线程启动,
同时线程B增加一个元素,这是modCount的值发生改变(modCount + 1 = N + 1)。 线程A继续遍历执行next方法时,
通告checkForComodification方法发现expectedModCount = N , 而modCount = N + 1,两者不等
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

源码分析:

每个实现Iterable接口的类必须提供一个iterator方法,返回一个Iterator对象,ArrayList也不例外

cursor--;
expectedModCount = modCount;

如图所示,Cursor初始状态是指向5,即若调用ArrayList的迭代器使用next()遍历数组,在遍历途中使用ArrayList.remove(1),则会跳原本应该遍历的5,直接遍历到6.采用上述代码后,它在每一次删除之后都会将cursor(下一项)的位置设置为当前位置,也就是将cursor往前移动了一位,之后再将modCount赋值给expectedModCount使它们保持相等。

Java集合框架分析(List)——ArrayList类详解

15.public int lastindexOf(Object elem);

作用:返回此列表中元素最后一次出现的位置下标,如果没有则返回-1
源码如下:

public int lastIndexOf(Object o) {
    if (o == null) {
        for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

源码分析:

若查找数组中指定元素最后一次出现的位置下标,从后往前遍历elementData数组,找到该元素第一次出现位置的索引值。

16.public ListIterator listIterator();

作用:从0开始,按当前集合中现有的元素进行迭代(双向迭代)
源码如下:


//inherited from java.util.AbstractList
public ListIterator<E> listIterator() {
    return listIterator(0);
}

public ListIterator<E> listIterator(final int index) {
    rangeCheckForAdd(index);//检查索引是否越界

    return new ListItr(index);//返回ListIter对象
}
private class ListItr extends Itr implements ListIterator<E> {
    ListItr(int index) {
        cursor = index;//cursor表示当前元素索引
    }

    public boolean hasPrevious() {
        return cursor != 0;
    }

    public E previous() {//返回当前元素的前一个元素
        checkForComodification();
        try {
            int i = cursor - 1;
            E previous = get(i);
            lastRet = cursor = i;
            return previous;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }

    public int nextIndex() {//返回当前元素后一个元素的索引值
        return cursor;
    }

    public int previousIndex() {//返回当前元素前一个元素的索引值
        return cursor-1;
    }

    public void set(E e) {//修改当前对象的属性
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            AbstractList.this.set(lastRet, e);
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    public void add(E e) {//向数组中添加对象
        checkForComodification();

        try {
            int i = cursor;
            AbstractList.this.add(i, e);
            lastRet = -1;
            cursor = i + 1; //分析类似17.public Iterator iterator();
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}

源码分析:

Iterator与ListIterator代码结构大同小异。其主要区别体现在以下:

(1)ListIterator有add()方法,可以向List中添加对象,而Iterator不能

(2)ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。

(3)ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。

(4)都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。

17.public ListIterator listIterator(final int index);

作用:返回指定下标(包含该下标)后的值
源码如下:

 public ListIterator<E> listIterator(final int index) {
    checkForComodification();//判断是否有线程修改数组
    rangeCheckForAdd(index);//判断索引是否越界
    final int offset = this.offset;

    return new ListIterator<E>() {
        int cursor = index;
        int lastRet = -1;
        int expectedModCount = ArrayList.this.modCount;

        public boolean hasNext() {//是否存在下一元素
            return cursor != SubList.this.size;
        }

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

        public boolean hasPrevious() {
            return cursor != 0;
        }

        @SuppressWarnings("unchecked")
        public E previous() {
            checkForComodification();
            int i = cursor - 1;
            if (i < 0)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (offset + i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i;
            return (E) elementData[offset + (lastRet = i)];
        }

        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = SubList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (offset + i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[offset + (i++)]);
            }
            // update once at end of iteration to reduce heap write traffic
            lastRet = cursor = i;
            checkForComodification();
        }

        public int nextIndex() {
            return cursor;
        }

        public int previousIndex() {
            return cursor - 1;
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                SubList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = ArrayList.this.modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.set(offset + lastRet, e);
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                SubList.this.add(i, e);
                cursor = i + 1;
                lastRet = -1;
                expectedModCount = ArrayList.this.modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

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

源码分析:

18.public boolean remove(Object o);

作用:移除指定元素
源码如下:

    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;
    }
    
      //快速删除es数组的第i个元素
     private void fastRemove(Object[] es, int i) {
        modCount++;
        final int newSize;
        if ((newSize = size - 1) > i)
            System.arraycopy(es, i + 1, es, i, newSize - i);
        es[size = newSize] = null;
    }

19.public Object remove(int index);

作用:删除指定位置的元素,后续元素往前补
源码如下:

    public E remove(int index) {
        Objects.checkIndex(index, size);
        final Object[] es = elementData;

        @SuppressWarnings("unchecked") E oldValue = (E) es[index];
        fastRemove(es, index);

        return oldValue;
    }

20.public boolean removeAll(Collection c);

作用:删除指定集合在此列表中的所有元素
源码如下:

//inherited from AbstractCollection
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);//判断要移除的集合是否为空
        return batchRemove(c, false);
    }

    /*
        @parameter:Collection c:目标集合
                    boolean complement:当前集合是否需要包含目标集合
                    false,则删除目标集合在当前集合中所存在的元素
                    true,则删除目标集合在当前集合中不纯在的元素
    */
    private boolean batchRemove(Collection<?> c, boolean complement)//
        final Object[] elementData = this.elementData;//定义当前数组对象
        int r = 0, w = 0;
        // r 在外层定义for循环所用下标,目的是为了判断有没有报错,如果报错了,r就!=size,如果没有报错r==size
        // w 进行操作的次数
        boolean modified = false;//定义成功标识
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)//目标集合中元素存在或不存在  在当前集合中    
                    elementData[w++] = elementData[r];//将不需要移除的元素放入elementData数组中
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {//表示报错了,与ArrayList的父类保持一致,当c.contains()这个方法抛出异常才会r != size
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);//复制数组,从报错下标开始复制到 w下标(最后被修改的下标),复制长度是未成功循环的长度
                w += size - r;//因为复制后数组长度就变了,所以需要求出目前数组的长度,w+ 复制的长度  
            }
            if (w != size) {// 表示原数组有删除了数据    
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    //从w开始循环,循环到size,这些数据是要删除的数据,设为null
                    elementData[i] = null;
                modCount += size - w;//修改操作次数计数 size-w表示删除的数量
                size = w; //将长度设为新的长度
                modified = true;
            }
        }
        return modified;
    }

源码分析:
1.判断要移除的集合是否为null,如果为空则抛出null异常

2.如果不为null,则调用batchRemove方法即可移除成功

2.1. 通过contains方法,将所有不需要移除的元素放入elementData数组中,然后标记最后一个放入元素的位置w

2.2. 将所有需要移除的元素设置为null

2.3. 将size的大小设置为w

2.4. 修改modCount字段(该阻断主要是为了防止多线程模式下安全问题)

2.5. 返回移除成功的标识

21.public boolean retainAll(Collection c);

作用:原数组和参数数组做交集,保留相同的部分。
源码如下:

//inherited from AbstractCollection
public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, true);//源码在20.public boolean removeAll(Collection c)处
}

源码分析:
ArrayList类中的retainAll(Collection c)重写了AbstractCollection中的此方法
private boolean batchRemove(Collection<?> c, boolean complement)部分源码:

for (; r < size; r++)
if (c.contains(elementData[r]) == complement)//目标集合中元素存在或不存在 在当前集合中
elementData[w++] = elementData[r];//将不需要移除的元素放入elementData数组中

removeAll()传入batchRemove中complement为false,表示将不存在的元素放入elementData数组
retainAll()传入batchRemove中complement为true,表示将存在的元素放入elementData数组中

22.public Object set(int index,Object element);

作用:用指定元素替换指定位置的元素。
源码如下:

public E set(int index, E element) {
    rangeCheck(index);//检查索引是否越界

    E oldValue = elementData(index);//将需要修改位置的值赋给oldValue
    elementData[index] = element;//将element覆盖初值
    return oldValue;
}

23.public int size();

作用:返回此列表中的元素数
源码如下:

public int size() {
    return size;
}

24.public List subList(int fromIndex,int toIndex);

作用:只返回从fromIndex到toIndex之间的数据元素。
源码如下:

    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);//索引边界检查
        return new SubList(this, 0, fromIndex, toIndex);
    }

    static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
    }

    //内部类SubList的构造函数:
        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {
            this.parent = parent;
            this.parentOffset = fromIndex;
            this.offset = offset + fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }

源码分析:

25.public Object[] toArray();

作用:将当前集合中的所有元素转化成*Object类型对象数组返回
源码如下:

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

源码分析:
ArrayList的数据结构本来就是一个数组,利用Object [] toArray()复制了一个对象

26.public Object[] toArray(Object []a);

作用:将集合中的元素依次存入参数数组中并返回。
源码如下:

    public <T> T[] toArray(T[] a) {
        if (a.length < size)//如果传入数组的长度小于size,返回一个新的数组,大小为size,类型与传入数组相同
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);//所传入数组长度与size相等,则将elementData复制到传入数组中并返回传入的数组。
        if (a.length > size)// 若传入数组长度大于size,除了复制elementData外,还将把返回数组的第size个元素置为空。

            a[size] = null;
        return a;
    }

27.public String toString();

作用:一个arraylist转换为字符串
源码如下:

//inherited from AbstractCollection
    public String toString() {
        Iterator<E> it = iterator();//集合本身调用迭代器方法(this.iterator),得到集合迭代器
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }

源码分析:
若集合没有元素->返回String类型"[]"
若集合有元素->先拼接'['->进入for死循环用append拼接e元素+','+' '->没有元素使用return跳出死循环并拼接']'

28.public void trimToSize();

作用:去除集合两端的null
源码如下:

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {//如果时间大小小于缓冲区容量的长度,则进行数组复制。
        elementData = (size == 0)
            ? EMPTY_ELEMENTDATA
            : Arrays.copyOf(elementData, size);
    }
}

源码分析:底层数组的容量调整为当前列表保存的实际元素的大小的功能

六.参考资料

ArrayList中的Iterator详解

Java 集合中常见 checkForComodification()方法的作用? modCount和expectedModCount作用?

java面试(ArrayList去重;hashcode与equals)

Java 集合中关于Iterator 和ListIterator的详解
ArrayList之removeAll底层原理实现详解
ArrayList中remove、removeAll、retainAll方法源码解析
用大白话告诉你ArrayList的底层原理