1. 程式人生 > >Java基礎List詳解

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