Java基础List详解

目录(本文基于JDK1.8)


 

 

1. 前言

我们在日常编程中,常常需要集中存放多个数据。这时候,数组是我们一个很好的选择,但是数组在初始化的时候指定了长度之后,这个数组长度就是不可变的,所以使用数组的话我们需要实现知道需要保存对象的数量。如果我们需要保存一个可以动态增长的数据,这个时候我们就需要集合类,这里我们就来学习一下集合类中的List

2. List基本概念

List继承自Collection接口,用来存储一组相同类型的元素,List集合代表一个元素有序、可重复的集合,它和Set的区别是:元素有放入顺序,元素可重复 。有顺序,即先放入的元素排在前面。

List主要有ArrayListLinkedListVector几种实现方式,这三者都实现了List接口。关于它们我们在,Java集合类里面有做简单的介绍。

2.1 ArrayList部分源码详解

2.1.1 ArrayList类的定义

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • ArrayList继承AbstractList抽象父类
  • 实现List接口规定了List的操作规范。
  • 实现RandomAccess(标记接口)代表ArrayList是支持快速随机访问。
  • 实现Cloneable(标记接口)代表ArrayList是可以拷贝。
  • 实现Serializable(标记接口)代表ArrayList是可以序列化的。

2.1.2 ArrayList字段属性

    /**
     * 默认初始容量.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 用于空实例的共享空数组实例(当用户指定该 ArrayList 容量为 0 时,返回该空数组).
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 默认大小空实例的共享空数组实例
     * 当用户使用无参构造函数创建ArrayList实例的时候,返回的是该数组。
     * 当用户第一次添加元素的时候,该数组将会扩容,变成容量为默认初始容量的一个数组。
     * 它与 EMPTY_ELEMENTDATA的区别就是:该数组是不指定容量返回的,而后者是在用户指 定容量为 0 时返回。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * ArrayList的容量是这个数组缓冲区的长度
     * 任何空数组(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
     * 在新增第一个元素的时候将会把容量扩充到DEFAULT_CAPACITY即10
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * ArrayList的大小(它包含的元素的数量).
     *
     * @serial
     */
    private int size;

2.1.3 ArrayList的构造方法

    /**
     *
     * 用指定的初始容量构造一个空的ArrayList.
     *
     * @param  initialCapacity  ArrayList的初始容量
     * @throws 如果指定的初始容量的值为负值则抛出 IllegalArgumentException 
     *         
     */
    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);
        }
    }

    /**
     * 无参构造函数
     *
     * 创建一个 空的 ArrayList,此时其内数组缓冲区 elementData = {}, 长度为 0,
     * 当元素第一次被添加的时候,扩容到默认容量10。
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 构造一个包含指定元素的列表的列表集合,按照集合的顺序返回迭代器
     *
     * @param c 要放入 ArrayList 中的集合,其内元素将会全部添加到新建的 ArrayList 实例中
     * @throws 如果指定的集合为null则抛出 NullPointerException
     */
    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的初始容量比较小,所以如果能预估数据量的话,分配一个较大的初始值属于最佳实践,这样可以减少调整大小的开销。

2.1.4 其他方法

    /**
     * 将这个ArrayList实例的容量变成ArrayList当前的大小
     * 
     * 因为容量常常会大于实际元素的数量。内存紧张时,可以调用该方法删除预留的位置,调整容量为元素实际数量。
     * 如果确定不会再有元素添加进来时也可以调用该方法来节约空间。
     */
    public void trimToSize() {
        //modCount是在其父类AbstractList中定义的,指list结构变化的次数,其中
        //结构变化指list的大小,即list包含元素的个数的变化
        modCount++;
        //先判断ArrayList里面的元素数量是不是小于容量
        if (size < elementData.length) {
            //如果size < 容量,判断size是不是等于0,如果等于0,容量为EMPTY_ELEMENTDATA
            //如果size不为0,容量为Arrays.copyOf(elementData, size)。
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

    /**
     * 使用指定参数设置ArrayList容量
     *
     * @param   minCapacity   所需最小容量
     */
    public void ensureCapacity(int minCapacity) {
        //如果数组为空,容量预取0,否则取默认的初始容量10。
        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);
        }
    }
    
  
    /**
     * 得到最小扩容量
     */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {   
        //如果elementData为空数组的时候,则返回初始容量和minCapacity其中大的一个
        //不为空则返回 minCapacity
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    /**
     * 扩容
     */
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    /**
     * 私有方法:明确 ArrayList 的容量
     * -用于内部优化,保证空间资源不被浪费:在 add() 方法添加时起效
     * @param minCapacity    指定的最小容量
     */
    private void ensureExplicitCapacity(int minCapacity) {
         将“list结构变化的次数”+1,该变量主要是用来实现fail-fast机制的
        modCount++;
        
        // 防止溢出代码:确保指定的最小容量 > 数组缓冲区当前的长度
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    /**
     * 数组最大容量,如果视分配更大可能会导致OOM
     *
     * 2^31 = 2,147,483,648,数组本身要占用 8 bytes存储大小,所以 2^31 -8
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
    /**
     * 扩容,以确保 ArrayList 至少能存储 minCapacity 个元素
     * 扩容计算:newCapacity = oldCapacity + (oldCapacity >> 1);  扩充当前容量的1.5倍.
     *
     * @param minCapacity 指定的最小容量
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        // 获取到ArrayList中elementData数组的内存空间长度
        int oldCapacity = elementData.length;
        //右移一位等于自身一半,所以扩容到原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 若 newCapacity 依旧小于 minCapacity,则用minCapacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 若 newCapacity 大于最大存储容量,则返回最大整数值
        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);
    }
    
    /**
     * 检查是否溢出,若没有溢出,返回最大整数值(java中的int为4字节,所以最大为0x7fffffff)或默认最大值.
     */
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
    
    /**
     * 返回ArrayList实际存储的元素数量.
     */
    public int size() {
        return size;
    }

    /**
     * ArrayList是否有元素
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 判断是否包含一个元素(根据 indexOf()判断)
     */
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

    /**
     * 返回一个值在数组首次出现的位置,会根据是否为null使用不同方式判断。不存在就返回-1。时间复杂度为O(N)
     */
    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;
    }
    
    /**
     * 返回一个值在数组最后一次出现的位置,会根据是否为null使用不同方式判断。不存在就返回-1。时间复杂度为O(N)
     */
    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;
    }

    /**
     * 返回这个ArrayList实例的一个浅副本。(元素本身不会被复制。)
     */
    public Object clone() {
        try {
            // 调用父类(翻看源码可见是Object类)的clone方法得到一个ArrayList副本
            ArrayList<?> v = (ArrayList<?>) super.clone();
            // 调用Arrays类的copyOf,将ArrayList的elementData数组赋值给副本的elementData数组
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            // 返回副本v
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
    
     /**
     * 转换为Object数组,使用Arrays.copyOf()方法.
     *
     *  @return 一个数组包含所有列表中的元素, 且顺序正确
     */
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
    
    /**
     * 如果a的长度小于ArrayList的长度,直接调用Arrays类的copyOf,返回一个比a数组长度要大的新数组,里面元素就是ArrayList里面的元素;
     *
     *如果a的长度比ArrayList的长度大,那么就调用System.arraycopy,将ArrayList的elementData数组赋值到a数组,然后把a数组的size位置赋值为空
     *
     * @param a 如果它的长度大的话,列表元素将存储在这个数组中; 否则,将为此分配一个相同运行时类型的新数组。
     * @return 一个包含ArrayList元素的数组
     * @throws 将与数组类型不兼容的值赋值给数组元素时抛出的异常ArrayStoreException 
     * @throws 如果指定的数组为空则抛出NullPointerException 
     */
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        // 若数组a的大小 < ArrayList的元素个数,则新建一个T[]数组,
        // 数组大小是"ArrayList的元素个数",并将“ArrayList”全部拷贝到新数组中
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        // 若数组a的大小 >= ArrayList的元素个数,则将ArrayList的全部元素都拷贝到数组a中。
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
    
    /**
      * 返回指定位置的值
      *
      * @param index
      * @return
      */
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

    /**
     * 返回ArrayList指定位置的元素.但是会先检查这个位置数否超出数组长度
     *
     * @param  index 返回元素的索引
     * @return 在这个ArrayList中指定位置的元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        // 检查是否越界
        rangeCheck(index);
    
        // 返回ArrayList的elementData数组index位置的元素
        return elementData(index);
    }

    /**
     * 设置指定位置为一个新值,并返回之前的值,会检查这个位置是否超出数组长度.
     *
     * @param index 要替换的元素的索引
     * @param element 现在要存储在指定位置的元素
     * @return 之前在指定位置的元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        // 检查是否越界
        rangeCheck(index);
        
        // 调用elementData(index)获取到当前位置的值
        E oldValue = elementData(index);
        // 将element赋值到ArrayList的elementData数组的第index位置
        elementData[index] = element;
        return oldValue;
    }
    
    /**
     * 增加指定的元素到ArrayList的最后位置
     *
     * @param e 要添加的元素
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        //确定ArrayList的容量大小
        //若list是第一次添加元素则,初始容量为默认的10
        //若不是第一次添加元素,则先扩容自身的1.5倍容量和初始容量比较
        //如果比初始容量小则取初始容量,如果比初始容量大则返回最大整数值
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 将e赋值给elementData的size+1的位置
        elementData[size++] = e;
        return true;
    }

    /**
     * 在这个ArrayList中的指定位置插入指定的元素,.
     *
     * @param index 指定元素将被插入的索引
     * @param element 要插入的元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        // 判断index是否越界
        rangeCheckForAdd(index);

        //确定ArrayList的容量大小
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //src:源数组; srcPos:源数组要复制的起始位置; dest:目的数组; destPos:目的数组放置的起始位置; length:复制的长度
        // 将elementData从index位置开始,复制到elementData的index+1开始的连续空间
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        // 在elementData的index位置赋值element
        elementData[index] = element;
        // ArrayList的大小加一
        size++;
    }

    /**
     *在ArrayList的移除index位置的元素,会检查添加的位置,返回之前的值
     *
     * @param index 要删除的元素的索引
     * @return 从ArrayList中删除的元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        // 判断是否越界
        rangeCheck(index);

        modCount++;
        //获取要删除元素的索引上的值
        E oldValue = elementData(index);
        //获取ArrayList内元素要移动的长度
        int numMoved = size - index - 1;
        if (numMoved > 0)
            // 将elementData数组index+1位置开始拷贝到elementData从index开始的空间
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 使size-1 ,设置elementData的最后一个位置为空,让GC来清理内存空间
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

    /**
     * 在ArrayList的移除对象为O的元素,跟indexOf方法思想基本一致
     *
     * @param o 要从该列表中删除的元素(如果存在)
     * @return true 如果这个列表包含指定的元素
     */
    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;
    }

    /*
     * 快速删除指定位置的值,之所以叫快速,应该是不需要检查和返回值,因为只内部使用
     */
    private void fastRemove(int index) {
        modCount++;
        //获取ArrayList内元素要移动的长度
        int numMoved = size - index - 1;
        if (numMoved > 0)
            // 将elementData数组index+1位置开始拷贝到elementData从index开始的空间
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 使size-1 ,设置elementData的最后一个位置为空,让GC来清理内存空间
        elementData[--size] = null; // clear to let GC do its work
    }

    /**
     * 清空数组,把每一个值设为null,方便垃圾回收(不同于reset,数组默认大小有改变的话不会重置)
     */
    public void clear() {
        //这个地方改变了modCount的值了
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            //便于垃圾回收器回收
            elementData[i] = null;
        //把size设置为0,以便我们不会浏览到null值的内存空间
        size = 0;
    }

    /**
     * 添加一个集合的元素到末端,若要添加的集合为空返回false
     *
     * @param c 包含要添加到此列表中的元素的集合
     * @return list 元素个数有改变时,成功:失败
     * @throws 如果指定的集合为null 则抛出NullPointerException
     */
    public boolean addAll(Collection<? extends E> c) {
        // 将c转换为数组a
        Object[] a = c.toArray();
        // 获取a占的内存空间长度赋值给numNew
        int numNew = a.length;
        //扩容原ArrayList的容量为size + numNew
        ensureCapacityInternal(size + numNew);  // Increments modCount
        // 将a的第0位开始拷贝至elementData的size位开始,拷贝长度为numNew
        System.arraycopy(a, 0, elementData, size, numNew);
        //将size的长度增加numNew
        size += numNew;
        // 如果c为空,返回false,c不为空,返回true
        return numNew != 0;
    }

    /**
     * 从第index位开始,将c全部拷贝到ArrayList,若要添加的集合为空返回false
     *
     * @param index 在哪个索引处插入指定集合中的第一个元素
     * @param c 包含要添加到此列表中的元素的集合
     * @return list 元素个数有改变时,成功:失败
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws 如果指定的集合是空的则抛出 NullPointerException 
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        // 判断index大于size或者是小于0,如果是,则抛出IndexOutOfBoundsException异常
        rangeCheckForAdd(index);

        // 将c转换为数组a
        Object[] a = c.toArray();
        int numNew = a.length;
        //扩容原ArrayList的容量为size + numNew
        ensureCapacityInternal(size + numNew);  // Increments modCount
        
        //获取ArrayList内元素要移动的长度
        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

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

    /**
     * 删除指定范围元素。参数为开始删的位置和结束位置
     *
     * @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) {
        modCount++;
        //获取ArrayList内元素要移动的长度
        int numMoved = size - toIndex;
        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++) {
            elementData[i] = null;
        }
        size = newSize;
    }

    /**
     * 检查index是否超出数组长度 用于添加元素时
     */
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    /**
     * 判断index大于size或者是小于0,如果是,则抛出IndexOutOfBoundsException异常
     */
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    /**
     * 抛出的异常的详情
     */
    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }

    /**
     * RArrayList移除集合c中的所有元素
     */
    public boolean removeAll(Collection<?> c) {
        // 如果c为空,则抛出空指针异常
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }
    
    /**
     * 仅保留指定集合c中的元素
     */
    public boolean retainAll(Collection<?> c) {
        // 如果c为空,则抛出空指针异常
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }
    
    /**
     * 根据complement值,将ArrayList中包含c中元素的元素删除或者保留
     *
     * @param c
     * @param complement true时从数组保留指定集合中元素的值,为false时从数组删除指定集合中元素的值。
     * @return 数组中重复的元素都会被删除(而不是仅删除一次或几次),有任何删除操作都会返回true
       */
    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++)
                // 如果c中不包含elementData[r]这个元素
                if (c.contains(elementData[r]) == complement)
                    // 则直接将r位置的元素赋值给w位置的元素,w自增
                    elementData[w++] = elementData[r];
        } finally {
            // 防止抛出异常导致上面r的右移过程没完成
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                // 将r未右移完成的位置的元素赋值给w右边位置的元素
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                // 修改w值增加size-r
                w += size - r;
            }
            // 如果有被覆盖掉的元素,则将w后面的元素都赋值为null
            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;
    }

    /**
     * 保存数组实例的状态到一个流(即序列化)。写入过程数组被更改会抛出异常
     *
     * @serialData The length of the array backing the <tt>ArrayList</tt>
     *             instance is emitted (int), followed by all of its elements
     *             (each an <tt>Object</tt>) in the proper order.
     */
    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();
        }
    }

    /**
     * 从流中重构ArrayList实例(即反序列化)
     */
    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
            // 像clone()方法 ,但根据大小而不是容量分配数组
            int capacity = calculateCapacity(elementData, size);
            SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, 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();
            }
        }
    }

    /**
     * 返回一个从index开始的ListIterator对象
     *
     * <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public ListIterator<E> listIterator(int index) {
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("Index: "+index);
        return new ListItr(index);
    }

    /**
     * 返回一个ListIterator对象,ListItr为ArrayList的一个内部类,其实现了ListIterator<E> 接口口
     *
     * <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     *
     * @see #listIterator(int)
     */
    public ListIterator<E> listIterator() {
        return new ListItr(0);
    }

    /**
     * 返回一个Iterator对象,Itr为ArrayList的一个内部类,其实现了Iterator<E>接口
     *
     * @return an iterator over the elements in this list in proper sequence
     */
    public Iterator<E> iterator() {
        return new Itr();
    }

2.2 什么是fail-fast,什么是fail-safe

上面源码里面我们提到了modcount++ 是为了实现fail-fast,现在我们看一下什么是 fail-fast

2.2.1 fail-fast (快速失败)

在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出ConcurrentModificationException

fail-fast会在以下两种情况下抛出ConcurrentModificationException

1、单线程环境 集合被创建后,在遍历它的过程中修改了结构。

2、多线程环境 当一个线程在遍历这个集合,而另一个线程对这个集合的结构进行了修改。

2.2.1.1 fail-fast机制是如何检测的?

迭代器在遍历过程中是直接访问内部数据的,因此内部的数据在遍历的过程中无法被修改。为了保证不被修改,迭代器内部维护了一个modCount变量 ,当集合结构改变(添加删除或者修改),modCount会被修改,而迭代器每次的hasNext()next()方法都会检查modCountt变量是否为expectedmodCount,如果是的话就返回遍历,如果检测到被修改时,抛出Concurrent Modification Exception

注意:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。

2.2.2 fail—safe (安全失败)

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历,因此不会抛出ConcurrentModificationException

2.2.2.1 fail—safe的原理

由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发ConcurrentModificationException

2.2.2.1 fail—safe的缺点

  • 1、因为迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的,也就是说无法保证读取的数据是目前原始数据结构中的数据。
  • 2、需要复制集合,产生大量的无效对象,开销大

注意:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

2.2.3 如何在遍历的同时删除ArrayList中的元素

Java中遍历集合的方法有好几种,例如普通for循环、增强for循环、迭代器以及lambda表达式的forecah,但是通过上面提到的fail-fast机制,我们知道遍历过程修改数据是会触发fail-fast的,那么如何如何在遍历的同时删除ArrayList中的元素呢?这里推荐使用迭代器遍历。

    Iterator<People> peo = peoples.iterator();    
    while (peo.hasNext()) {    
        People people = peo.next();    
        if (people.getId() == 2)    
            people.remove();//这里要使用Iterator的remove方法移除当前对象,如果使用List的remove方法,则同样会出现ConcurrentModificationException    
    }

2.3 LinkedList部分源码详解

2.3.1 LinkedList类的定义

    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable

  • LinkedList继承了AbstractSequentialList,它可以被当作堆栈、队列或双端队列进行操作。
  • 实现List接口规定了List的操作规范。
  • 实现 Deque 接口代表LinkedList可以当作双端队列使用。
  • 实现Cloneable(标记接口)代表LinkedList是可以拷贝。
  • 实现Serializable(标记接口)代表LinkedList是可以序列化的。

2.3.2 LinkedList字段属性

    /**
     * size是双向链表中节点实例的个数,transient关键字表示size不会被序列化
     */
    transient int size = 0;

    /**
     * 链表头节点。
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * 链表尾节点。
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;
    
    
    /**
     * Node内部类
     */
    private static class Node<E> {
        //当前节点的值
        E item;
        // 后一个节点
        Node<E> next;
        // 前一个节点
        Node<E> prev;
        // 构造函数元素顺序分别为前一个节点,自己,后一个节点。
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

2.3.3 LinkedList的构造方法

    /**
     * 无参构造方法.
     */
    public LinkedList() {
    }

    /**
     * 构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回的顺序排列的
     *
     * @param  c 包含用于去构造 LinkedList 的元素的 collection
     * @throws 如果指定的集合为 null 则抛出 NullPointerException 
     */
    public LinkedList(Collection<? extends E> c) {
        // 调用无参构造函数
        this();
        // 添加集合中所有元素
        addAll(c);
    }

2.3.4 其他方法


    /**
     * 头插入,即将节点值为e的节点设置为链表首节点,内部使用
     */
    private void linkFirst(E e) {
        //获取当前首节点引用
        final Node<E> f = first;
        
        //构建一个prev值为null,节点值为e,next值为f的新节点newNode
        final Node<E> newNode = new Node<>(null, e, f);
        //将newNode作为首节点
        first = newNode;
        //如果原首节点为null,即原链表为null,则链表尾节点也设置为newNode
        //否则,原首节点的prev设置为newNode
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        //长度加+1
        size++;
        //修改次数+1
        modCount++;
    }
    
    /**
     * 尾插入,即将节点值为e的节点设置为链表的尾节点.
     */
    void linkLast(E e) {
        // 获取当前尾结点引用
        final Node<E> l = last;
        //构建一个prev值为l,节点值为e,next值为null的新节点newNode
        final Node<E> newNode = new Node<>(l, e, null);
        //将newNode作为尾节点
        last = newNode;
        //如果原尾节点为null,即原链表为null,则链表首节点也设置为newNode
        //否则,原尾节点的next设置为newNode
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        //长度加+1
        size++;
        //修改次数+1
        modCount++;
    }
    
    /**
     * 在非空节点succ之前插入元素e.
     */
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        //获取succ前一个节点
        final Node<E> pred = succ.prev;
        //构建一个prev值为pred,节点值为e,next值为succ的新节点newNode
        final Node<E> newNode = new Node<>(pred, e, succ);
        //把newNode作为prev的前一个节点
        succ.prev = newNode;
        //如果succ.prev为null,即如果succ为首节点,则将newNode设置为首节点
        //否则原来succ前一个节点的下一个节点为newNode
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }
    
    
    /**
     * 删除首节点并返回该元素,内部使用
     */
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        //获取首节点的值
        final E element = f.item;
        //获取首节点的下一个节点
        final Node<E> next = f.next;
        //删除首节点
        f.item = null;
        f.next = null; // help GC
        //原来首节点的下一个节点设置为首节点
        first = next;
        // 如果原来首结点的后继结点为空,则尾结点设为null
        // 否则,原来首结点的后继结点的前驱结点设为null
        if (next == null)
            last = null;
        else
            next.prev = null;
        //长度 - 1
        size--;
        //修改次数 +1
        modCount++;
        return element;
    }
    
    /**
     * 删除尾节点并返回该元素,内部使用
     */
    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        //获取为尾节点的值
        final E element = l.item;
        //获取尾节点上一个节点
        final Node<E> prev = l.prev;
        //删除尾节点
        l.item = null;
        l.prev = null; // help GC
        //原来尾节点上一个节点设置为尾节点
        last = prev;
        // 如果原来尾结点的前驱结点为空,则首结点设为null
        // 否则,原来尾结点的前驱结点的后继结点设为null
        if (prev == null)
            first = null;
        else
            prev.next = null;
        //长度 - 1
        size--;
        //修改次数 +1
        modCount++;
        return element;
    }
    
    /**
     * 删除指定非空节点并返回该元素
     */
    E unlink(Node<E> x) {
        // assert x != null;
        //获取指定节点的值
        final E element = x.item;
        //获取指定节点下一个节点
        final Node<E> next = x.next;
        //获取指定节点前一个节点
        final Node<E> prev = x.prev;
        //如果指定节点前一个节点为null,则首节点为指定节点的下一个节点
        //否则指点节点的前节点的下一个节点为指定节点的下一个节点
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }
        //如果指定节点下一个节点为null,则尾节点为指点节点的前一个节点。
        //否则指点节点的前一个节点为,指点节点的前一个节点
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
        //删除指定节点的值
        x.item = null;
        //长度减1
        size--;
        //修改次数加1
        modCount++;
        return element;
    }
    
    /**
     * 获list 取首节点存储的值.
     *
     * @return 这个列表中的第一个元素
     * @throws 如果这个list是空的则抛出 NoSuchElementException 
     */
    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

    /**
     * 获取list 尾节点存储的值
     *
     * @return 这个列表中的最后一个元素
     * @throws 如果这个list是空的则抛出 NoSuchElementException
     */
    public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }
    
    /**
     * 删除首节点并返回首节点存储的值
     *
     * @return 首结点存储的值 
     * @throws 如果这个list是空的则抛出 NoSuchElementException
     */
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
    
    /**
     * 删除尾节点并返回尾节点存储的值
     *
     * @return 尾节点存储的值
     * @throws 如果这个list是空的则抛出 NoSuchElementException
     */
    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }
    
    /**
     * 在list的开头插入指定的元素.
     *
     * @param e 需要添加的元素
     */
    public void addFirst(E e) {
        linkFirst(e);
    }

    /**
     * 在列表的尾部添加指定元素,该方法等价于add()
     *
     * @param e 需要添加的元素
     */
    public void addLast(E e) {
        linkLast(e);
    }

    /**
     * 判断是否包含指定元素
     *
     * @param o 判断链表是否包含的元素
     * @return {@code true} 如果链表包含指定的元素
     */
    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }

    /**
     * 返回list中的元素数量.
     *
     * @return 列表中元素的数量
     */
    public int size() {
        return size;
    }

    /**
     * 插入指定元素,返回操作结果,默认添加到末尾作为最后一个元素
     *
     * @param e 要添加到此链表中的元素
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        // 通过尾插法来插入指定元素
        linkLast(e);
        return true;
    }
    
     /**
     * 删除指定元素,默认从first节点开始,删除第一次出现的那个元素
     *
     * @param o 要从该列表中删除的元素(如果存在)
     * @return {@code true} 如果这个列表包含指定的元素
     */
    public boolean remove(Object o) {
        //会根据是否为null分开处理。若值不是null,会用到对象的equals()方法
        if (o == null) {
            // 遍历链表,查找到指定元素后删除该结点,返回true
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 将集合插入到链表尾部,即开始索引位置为size
     *
     * @param c 包含要添加到此链表中的元素的集合
     * @return {@code true} 如果该链表因添加而改变
     * @throws 如果指定的集合是空的则抛出NullPointerException
     */
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }

    /**
     * 将集合从指定位置开始插入
     *
     * @param index 在哪个索引处前插入指定集合中的第一个元素
     *              
     * @param c 包含要添加到此链表中的元素的集合
     * @return {@code true} 如果该链表因添加而改变
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws 如果指定的集合是空的则抛出 NullPointerException
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        //检查该索引是否超过了list里面元素的数量,如果超出了则抛出 IndexOutOfBoundsException
        checkPositionIndex(index);

        // 将集合转换为Object数组
        Object[] a = c.toArray();
        // 获取数组长度
        int numNew = a.length;
        //若没有元素要添加,直接返回false
        if (numNew == 0)
            return false;
        
        //succ指向当前需要插入节点的位置,pred指向其前一个节点
        Node<E> pred, succ;
        //如果是在末尾开始添加,当前节点后一个节点初始化为null,前一个节点为尾节点
        //否则当前位置的节点为指定位置的节点,前一个节点为要添加的节点的前一个节点
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }
        //遍历数组并添加到列表中
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            //将元素值e,前继节点pred“封装”为一个新节点newNode
            Node<E> newNode = new Node<>(pred, e, null);
            //如果原链表为null,则新插入的节点作为链表首节点
            //否则前节点会向后指向新加的节点
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }
        
        //如果是从最后开始添加的,则最后添加的节点成为尾节点
        if (succ == null) {
            last = pred;
        } else {
            //如果不是从最后开始添加的,则最后添加的节点向后指向之前得到的后续第一个节点
            pred.next = succ;
            //当前,后续的第一个节点也应改为向前指向最后一个添加的节点
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
    }

    /**
     * 删除所有元素
     */
    public void clear() {
        //遍历链表,删除所有结点,方便gc回收垃圾
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        //删除首尾节点
        first = last = null;
        //元素数量置为0
        size = 0;
        modCount++;
    }


    // Positional Access Operations

    /**
     * 获取指定位置的元素
     *
     * @param index 要返回的元素的索引
     * @return 该链表中指定位置的元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        // 判断指定位置是否合法
        checkElementIndex(index);
        //返回指定位置的值
        return node(index).item;
    }

    /**
     * 修改指定位置的元素,返回之前元素
     *
     * @param index 要替换的元素的索引
     * @param element 要存储在指定位置的元素
     * @return 先前在指定位置的元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        //判断位置是否合法
        checkElementIndex(index);
        //获取指定位置的节点
        Node<E> x = node(index);
        //获取指定位置的值
        E oldVal = x.item;
        //将指定位置的值更新为新值
        x.item = element;
        return oldVal;
    }

    /**
     * 在指定位置前插入指定元素
     *
     * @param index 指定元素将被插入的索引
     * @param element 要插入的元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        //判断位置是否合法
        checkPositionIndex(index);
        //如果指定位置在尾部,则通过尾部插入法插入元素
        //否则通过中间插入法插入元素
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

    /**
     * 删除指定位置的元素,返回之前元素
     *
     * @param index 要删除的元素的索引
     * @return 之前在该位置的元素
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        //判断位置是否合法
        checkElementIndex(index);
        //删除指定非空节点并返回该元素
        return unlink(node(index));
    }
    
    /**
     * 判断指定位置是否合法.
     */
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

    /**
     * 判断迭代器遍历时或插入元素时指定位置是否合法
     */
    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }

    /**
     * Constructs an IndexOutOfBoundsException detail message.
     * Of the many possible refactorings of the error handling code,
     * this "outlining" performs best with both server and client VMs.
     */
    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }

    
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值