ArrayList源码浅析 Base1.8

344 阅读15分钟

ArrayList简介

  • ArrayList一种支持自动扩容的动态数组,继承了AbstractList实现了List,RandomAccess, Cloneable,java.io.Serializable,故支持随机访问,可克隆,可序列化等特性
  • 粗略地讲,ArrayList和Vector功能基本等价,但是不同的是,ArrayList在多线程中式不安全,而Vector是线程安全的。如果存在多个线程访问ArrayList实例,且存在一个线程在修改该实例结构。可考虑使用同步的list或外部加锁或使用List list = Collections.synchronizedList(new ArrayList())
  • fail-fast机制:iterator ListIterator 两种迭代在遍历列表时,发现此列表结构正在别改变(增加元素add addll,删除元素remove removeIf removeAll retainAll,清除clear都可以认为时结构改变)就会抛出一个ConcurrentModificationException异常,称这样机制为fail-fast机制
  • 可存入null
  • java8 引入了函数式编程,ArrayList对元素的处理更灵活
  • ArrayList:说明ArrayList支持泛型
  • ArrayList继承了AbstractList,AbstractList是List接口的骨干实现,以最大限度地减少“随机访问”数据存储(如ArrayList)实现Llist所需的工作
  • implements List:实现了List接口,实现了所有可选列表操作
  • implements RandomAccess:实现RandomAccess接口,说明ArrayList支持快速(通常是固定时间)随机访问,此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能
  • implements Cloneable:实现Cloneable接口,表明其可以调用clone()方法来返回实例的field-for-field拷贝
  • implements java.io.Serializable:实现Serializable接口,说明ArrayList具有序列化功能

检测结构改变的方法是:ArrayList类从AbstraList父类继承了一个modCount,每次执行add remove等方法改变结构时,modCount都会记录共修改了列表结构多少次,(如执行add(E e) 时,增加了一个元素,modCounth会加1),迭代器初始化时,会令一个成员变量int expectedModCount = modCount 每次迭代数组时(如执行next() previous()等方法),都会检测一下expectedModCount 是否 仍然等于 modCount(调用checkForComodification()),若发现不等说明,立即抛出ConcurrentModificationException,当然这种利用迭代器遍历时,尽最大努力的保障fail-fast机制。(尽最大努力 是由于并发的不确定性导致的,如刚刚检查完expectedModCount modCount的大小后,另外一个线程修改了列表结构,可能就检查不到)

ArrayList基础属性

1. 基础字段

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     */
    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.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    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;
  • DEFAULT_CAPACITY 默认初始化容量* EMPTY_ELEMENTDATA 初始空数组,当利用ArrayList(int initialCapacity)构造函数初始化时,elementData = EMPTY_ELEMENTDATA* DEFAULTCAPACITY_EMPTY_ELEMENTDATA 默认初始容量下的空数组,这样我们知道在添加第一个元素的时候,应该扩容多少,当利用ArrayList<>()构造函数初始化时,elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA* elementData 存储ArrayList元素的数组,其长度(elementData.length())为ArrayList数组的容量* size list的容量大小,ArrayList中存放的实际元素个数* MAX_ARRAY_SIZE 有些虚拟机种支持ArrayList中允许最大的size,有些虚拟机也可以扩容为Integer.MAX_VALUE* protected transient int modCount = 0; 从AbstractList继承而来,主要用于fail-fast机制,检查列表结构在迭代过程中有没有被修改

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) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c 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();
        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(int initialCapacity) ArrayList(int) 参数为初始化列表大小,默认情况为10, 当需要反复扩容时,建议先预估列表大小,提高性能* ArrayList() 构造一个初始化容量为10的列表* ArrayList(Collection<? extends E> c) 构造一个有具体元素的列表

ArrayList API概述

1. Override或新增的public方法

void            trimToSize() //将列表的容量收缩为size,去掉capacity中没用元素用的位置
void            ensureCapacity(int minCapacity) //确保列表的容量可容纳所有元素,否则扩容
int             size() //返回列表大小
boolean         isEmpty() //列表为空返回true,否则false
boolean         contains(Object o) //判断列表是否包含对象o,
int             indexOf(Object o)  //顺序查找,返回对象o在列表中首次出现的位置, o不在列表中返回-1
int             lastIndexOf(Object o) //逆序查找,返回对象o在首次出现的位置,不在返回-1
Object          clone() //浅拷贝列表,如果列表中是基本类型,就是深拷贝,若是自定义类,就是浅拷贝
Object[]        toArray() //列表转数组,是数组和列表之间的桥梁
<T> T[]         toArray(T[] a) //列表转数组,返回数组的运行时类型是指定数组的运行时类型。
E               elementData(int index) //按位置返回,对index的范围不做检查,返回第index位置上的元素
E               get(int index) //按位置返回,检查index的范围,返回第index位置上的元素
E               set(int index, E element) //将index位置的元素换为element,返回原来的element
boolean         add(E e) // 增加element新元素
void            add(int index,E element) //按位置插入,在index位置插入element,从index位置(包括index位置)开始整体后移1位
E               remove(int index) //按位置移除,将index位置的元素移除,从index+1位置开始每个元素依次往前移动1位
boolean         remove(Object o) //按元素移除,顺序找到待移除元素o,移除后,后面元素依次往前移动1位
void            clean() //清空列表
boolean         addAll(Collection<? extends E> c) //增加一个集合(多个元素),【先时后面的每个元素依次往后移动多个位置让出空间】
boolean         addAll(int index, Collection<? extends E> c) //从index位置开始,增加一个集合(多个元素)【先时后面的每个元素依次往后移动多个位置让出空间】
boolean         removeAll(Collection<? extends E> c) //如果列表中的元素存在在集合c中,那么将从列表中移除,即为保留列表与c的差集[列表-c]
boolean         retainAll(Collection<? extends E> c) //如果列表中的元素存在在集合c中,那么将保留,即为取列表和c的交集
ListIterator<E> listIterator(int index) //双向迭代器【既可以前移,也可以往后移】,从第index位置开始迭代,可以利用该类型迭代器增插删元素【由于实现get,set,remove】 
ListIterator<E> listIterator() //双向迭代器【既可以前移,也可以往后移】,从0位置开始迭代,
Iterator<E>     iterator() //单向(顺序从前往后),可以利用该类型迭代器移除元素【由于实现remove】
List<E>         subList(int fromIndex, int toIndex) //返回列表的一个子列表
void            forEach(Consumer<? super E> action) //遍历每个元素,对每个元素做action操作
Spliterator<E>  spliterator()
boolean         removeIf(Predicate<? super E> filter) //移除列表中符合filter动作的元素
void            replaceAll(UnaryOperator<E> operator) 
void            sort(Comparator<? super E> c) //根据c的规则排序

2. 超类或接口直接继承的default和常规方法

public int              hashCode()  //从AbstractList类直接继承,且ArrayList类中没有Override
public boolean          equals(Object O) //从AbstractList类直接继承,且ArrayList类中没有Override
default Stream<E>       stream()  //串行流
default Stream<E>       parallelStream()  //并行流

3. ArrayList类内部private方法

void             ensureCapacityInternal(int minCapacity) //检查扩容
static int       calculateCapacity(Object[] elementData ,int) //检查是否需要扩容
void             ensureExplicitCapacty(int),grow(int ) //检查是否需要扩容
void             grow(int minCapacity)  //常规扩容+ 按需扩容
static int       hugeCapacity(int minCapacity) //当容量快达最大容量时的扩容操作
void             fastRemove(int index) //按位置快速remove,快速体现在不对index做范围检查
void             rangeCheck(int index)  //对index做边界检查,防止越上界
void             rangeCheckForAdd(int index) //对index做边界检查,防止越上下界
String           outOfBoundsMsg(int index) //越界异常的提醒信息
boolean          batchRemove(Collection<?> c, boolean complement) //批量remove,主要为removeAll retainAll两个public方法服务
void             writeObject(java.io.ObjectOutputStream s) //序列化 
void             readObject(java.io.ObjectInputStream s)  //序列化

ArrayList源码详解

1. add()方法

//添加元素到list末尾
public boolean add(E e) {
    //确保list的容量大小,如果当前空间不足,容量加1
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //将新元素添加在末尾位置
    elementData[size++] = e;
    return true;
}

//在指定位置插入元素。当前位置的元素和index之后的元素向后移一位
public void add(int index, E element) {
    rangeCheckForAdd(index);

    //确保list的容量大小,如果当前空间不足,容量加1。
    ensureCapacityInternal(size + 1);  
    //将index位置之后的元素后移
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    //将元素添加在index位置
    elementData[index] = element;
    //容量加1
    size++;
}

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

// 数组容量检查,不够时则进行扩容
private void ensureCapacityInternal(int minCapacity) {
    // 如果是空数组,取默认容量与所需容量的最大值为实际所需容量
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

// 数组容量检查,不够时则进行扩容
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // 如果实际所需容量大于数组容量,扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

//扩容,保证ArrayList至少能存储minCapacity个元素
private void grow(int minCapacity) {
    // 获取当前数组的容量
    int oldCapacity = elementData.length;
    // 容量扩容为原来的1.5倍,新的容量=当前容量+当前容量/2,即将当前容量增加一半
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果扩容后的容量还是小于实际所需容量,则将扩容后的容量设置为实际所需容量
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 如果扩容后的容量超过了系统预设的最大值:Integer.MAX_VALUE - 8,检测是否溢出
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 使用Arrays的copyof方法将原数组数据拷贝到新的数组,并将新数组赋值给变量elementData
    elementData = Arrays.copyOf(elementData, newCapacity);
}

// 进行大容量分配
private static int hugeCapacity(int minCapacity) {
    // 如果溢出,提示异常,如果没有溢出,实际所需容量是否超过系统预设的最大值,如果超过,返回Integer的最大值,如果没有超过,返回系统预设的最大值
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    //如果想要的容量大于MAX_ARRAY_SIZE,则分配Integer.MAX_VALUE,否则分配MAX_ARRAY_SIZE
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

add方法实现大致流程:

  • 判断数组容量是否为空,如果是,比较默认容量与实际容量大小,取最大值
  • 判断实际所需容量如果大于数组容量,扩容
  • 保存数据,size加1

2. get()方法

//返回list中索引为index的元素
public E get(int index) {
    // 检查是否越界
    rangeCheck(index);
    //根据索引返回元素
    return elementData(index);
}

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

3. remove()方法

//删除list中位置为指定索引index的元素
public E remove(int index) {
    //检查索引是否越界。如果参数指定索引index>=size,抛出一个越界异常
    rangeCheck(index);
    //结构性修改次数+1
    modCount++;
    //记录索引为inde处的元素
    E oldValue = elementData(index);
    // 删除指定元素后,需要左移的元素个数
    int numMoved = size - index - 1;
    //如果有需要左移的元素,就移动(移动后,该删除的元素就已经被覆盖了)
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // size减一,然后将索引为size-1处的元素置为null。为了让GC起作用,必须显式的为最后一个位置赋null值
    elementData[--size] = null; // clear to let GC do its work
    //返回被删除的元素
    return oldValue;
}

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

4. removeAll()方法

public boolean removeAll(Collection<?> c) {
    // 参数非空校验
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}

private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try {
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
       // 如果c.contains提示异常
       if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        // 如果有数据被删除
        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;
}

removeAll方法的实现在于,先遍历elementData,将elementData与另一个集合c没有交集的数据,放置在elementData的下标的0到w段,然后再清除掉下标w到size-1之间的元素就行了(即设置为null)

5. set()方法

//替换指定索引的元素
public E set(int index, E element) {
    //检查索引是否越界。如果参数指定索引index>=size,抛出一个越界异常
    rangeCheck(index);

    //记录被替换的元素
    E oldValue = elementData(index);
    //替换元素
    elementData[index] = element;
    //返回被替换的元素
    return oldValue;
}

6. trimToSize()方法

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}
  • 我们对数组扩容之后,有时候数组的容量会大于实际所需要的容量,这时候如果我们想将数组容量调整为实际所需要的容量,可以调用该方法* 比如内存紧张,或者我们可以确定不会再有元素添加进来时,也可以调用该方法来节省空间

7. ensureCapacity(int minCapacity)方法

public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        // any size if not default element table
        ? 0
        // larger than default for default empty table. It's already
        // supposed to be at default size.
        : DEFAULT_CAPACITY;

    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}
  • 从add()与addAll()方法中可以看出,每当向数组中添加元素时,都要去检查添加元素后的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求* 在JDK8中,JDK提供了一个public的ensureCapacity方法让我们可以手动设置ArrayList的容量,以减少上面这种递增时调用再重新分配的数量

8. 其他

由于ArrayList实现了RandomAccess, Cloneable, java.io.Serializable,所以支持随机读取,复制,序列化操作,对应一些方法:

/**
 * 按下标读取
 */
public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

/**
 * 复制
 */
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);
    }
}

/**
 * 序列化
 */
private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

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

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

ArrayList 扩容

ArrayList的扩容机制简单来讲,就是我们在添加元素前,会检查现有的容量(容量就是elementData.length())够不够,不够,容量就扩大为原来的1.5倍,够了,就直接添加。扩容的具体细节由ArrayList中如下几个私有函数完成:

  • ensureCapacityInternal(int minCapacity)
  • calculateCapacity(Object[] elementData ,int)
  • ensureExplicitCapacty(int)
  • grow(int )
  • hugeCapacity(int)

1. ensureCapacityInternal(int minCapacity)

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

minCapacity指需要的最小容量,如使用add增加1个元素,minCapacity=size+1,使用addAll增加n个元素,minCapacity=size+n。

肯定的讲,只要添加元素(add addAll),添加前,一定会检查是否需要扩容,add调用ensureCapacityInternal检查是否需要扩容,addAll直接调用ensureExplicitCapacity是否需要扩容。

2. calculateCapacity(Object[] elementData ,int)

private static int calculateCapacity(Object[] elementData, int minCapacity) { //elementData就是我们存放列表的地方(值或者引用)
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity); 
    }
    return minCapacity;
}

当我们使用无参构造函数ArrayList<>()初始化类时,会执行this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,根据上面DEFAULTCAPACITY_EMPTY_ELEMENTDATA字段定义,刚刚初始化时,elementData的容量为0,当第一次add时,直接扩容为10(即elementData.length = 10),后面当minCapacity大于10时,都不会扩容,当minCapacity大于10时,newCapacity = element.length()>>1 + elementData.length(),即扩容成原容量的1.5倍。

3. ensureExplicitCapacty(int)

private void ensureExplicitCapacity(int minCapacity) {
    modCount++; //当结构可能会改变时,modCount也修改了,多线程环境下,某个线程迭代器可能检测到,执行fail-fast机制
    // overflow-conscious code
    if (minCapacity - elementData.length > 0) //需要的最小容量都大于现有容量,那就需要扩容,这条语句会有数值溢出风险
        grow(minCapacity);
}

4. grow(int )

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) //当扩容达到允许的容量上限后,(MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ),不在按照1.5倍扩容,也不按需扩容
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

5. hugeCapacity(int)

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow 
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?   //如果大于 MAX_ARRAY_SIZE,直接扩充成Integer.MAX_VALUE,否则直接扩为 MAX_ARRAY_SIZE
        Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}

小结

首次利用无参初始化ArrayList示例的时候,列表容量为0,当加入第一个元素时,立即扩容为10,以后每次需要的最小容量达到当前容量时,每次按当前容量1.5倍扩容,当扩容扩到一定程度时候,理想情况下,会出现两种情况:第一种扩容后,新容量比原容量小,则执行按需扩容(有多少元素,就开多大空间),第二种扩容后,容量超过容量上限,如果原容量小于容量上限就直接扩充成容量上限,如果原容量已经大于容量上限,直接扩成Integer.MAX_VALUE。

值得考虑的是,这种"弹性"的扩容机制在极大数据量下肯定是不安全的,比如,我们目前已经扩到Integer.MAX_VALUE,然后我们在add一个元素(minCapacity = -2147483648),从ensureCapacityInternal走一套下来,我们发现添加不进去,原数组也不会变,这无可厚非,但是在大数据情况下,这种扩容是否是完备的呢?即我们在不超过Integer.MAX_VALUE时,是否总能安全存放数据?我认为扩容后只要不溢出且不达到最大容量,都是安全的,否则只要溢出数据就不安全了。

ArrayList 迭代

ArrayList中实现了两种迭代器:Iterator,ListIterator。我们可以通过iterator() ,listIterator() 函数得到这两种两种迭代器。

迭代器源码浅析

  • iterator()源码

    public Iterator iterator() { return new Itr(); //返回一个匿名Itr()类 }

  • listIterator()源码

    public ListIterator listIterator() { return new ListItr(0); //从第0位置开始迭代 }

  • listIterator(int index)源码

    public ListIterator listIterator(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index); return new ListItr(index); //从index位置开始迭代 }

  • ArrayList Itr内部类源码

    private class Itr implements Iterator { int cursor; // index of next element to return 将要返回的元素的索引 int lastRet = -1; // index of last element returned; -1 if no such 上一个已返回的元素的索引,如果还没开始迭代,初始值为-1 int expectedModCount = modCount; //初始化expectedModCount = modCount,检测列表结构是否被改变 Itr() {} //无参空的构造函数 public boolean hasNext() { return cursor != size; //cursor == size 表示目前已到列表尾部 }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification(); //检测 expectedModCount 是否等于modCount,相等则认为目前无其他线程在修改此列表结构,否则直接抛异常
            int i = cursor;   
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData; //让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();
            }
        }
    
        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {  //得到Itr()类后,我们可以iterator.forEachRamaining(System.out::println)
            Objects.requireNonNull(consumer);                         //由于Comsumer函数式接口中的accept没有返回值,所以不会对this.elementData做任何修改
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
    
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
    

注意到使用iterator()返回的Itr()匿名内部类,只可以对列表元素进行遍历和移除操作。想要得到列表的索引,一定要先执行next()

  • ArrayList ListItr内部类源码

    private class ListItr extends Itr implements ListIterator { //继承了内部类Itr(),[cursor,lasRet,hasNext,next,remove,forEachRemaining] ListItr(int index) { super(); cursor = index; }

        public boolean hasPrevious() {
            return cursor != 0;
        }
    
        public int nextIndex() { //返回cursor位置
            return cursor;
        }
    
        public int previousIndex() { //返回cursor-1位置
            return cursor - 1;
        }
    
        @SuppressWarnings("unchecked")
        public E previous() {  //往前迭代
            checkForComodification();
            int i = cursor - 1;
            if (i < 0)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i;
            return (E) elementData[lastRet = i];
        }
    
        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
    
            try {
                ArrayList.this.set(lastRet, e);
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    
        public void add(E e) {
            checkForComodification();
    
            try {
                int i = cursor;
                ArrayList.this.add(i, e);
                cursor = i + 1;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }
    

调用listIterator(int index),可以得到ListIterator迭代器,这是一个双向迭代器,cursor既可以往前,也可以往后。Iterator是一个顺序单想迭代器,只能往后。不管时ListIterator还是Iterator在想要得到索引之前,一定要先执行next()或previous(),利用listIterator迭代器可以对列表实现增删改查操作,Iterator迭代器可以对列表实现remove操作,通过源码可以发现这些操作实质都是对原列表直接操作的。

其他

  • int hashCode() 和 boolean equals(Object o)

这两个方法都是直接继承自AbstractList,ArrayList没有重写Override 者两个方法

hashCode()

public int hashCode() {
    int hashCode = 1;
    for (E e : this)  //遍历每个元素,计算一个总的hashCode
        hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());  //e.hashCode()的计算根据e的类型
    return hashCode;
}

若 e 是 Integer类型,则e.hashCode就是数字本身,如 Integer i = 20,i.hashCode()就是20,其他类型如Double Float String hashCode的实现方法各异。

equals(Object o)

public boolean equals(Object o) {
    if (o == this) //指向同一个引用,直接认为相等
        return true;
    if (!(o instanceof List))  //如果都不是实现了List接口的类或子类,直接认为不相等
        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());   //其中一个较短的已经比较完,且两个列表都相等,若其中一个列表还有下个元素,则返回false,否则返回true   
}

equals 和 hashCode 具有相同的语义,即为 a.equals(b)为 true,则 a.hashCode() 一定等于 b.hashCode(),所以这两个方法在类中一定是同时重写的。

补充

ArrayList其他API

void            forEach(Consumer<? super E> action) //遍历每个元素,对每个元素做accept操作
Spliterator<E>  spliterator() //to do list
boolean         removeIf(Predicate<? super E> filter) //移除列表中符合filter动作的元素
void            replaceAll(UnaryOperator<E> operator) 
void            sort(Comparator<? super E> c) //根据c的规则排序
Object[]        toArray() //列表转数组,是数组和列表之间的桥梁
<T> T[]         toArray(T[] a) //列表转数组,返回数组的运行时类型是指定数组的运行时类型。