集合框架原始碼學習之ArrayList

SnailClimb發表於2018-03-19

目錄:

0-0-1. 前言

0-0-2. 集合框架知識回顧

0-0-3. ArrayList簡介

0-0-4. ArrayList核心原始碼

0-0-5. ArrayList原始碼剖析

0-0-6. ArrayList經典Demo

前言:

  這篇文章,其實幾天前就已經在圖書館寫出來了,不過手一抖幾個刪除鍵就都沒有了,所以一直拖到了現在。這篇文章在分析ArrayList的時候對ArrayList原始碼中用到的比較好的語法也會作以陳述。希望通過這篇文章可以讓你從本質上認識ArrayList,筆者愚笨,如若遇到錯誤敬請告知。

集合框架知識回顧:

總體知識脈絡

集合框架

ArrayList簡介:

  ArrayList 的底層是陣列佇列,相當於動態陣列。與Java中的陣列相比,它的容量能動態增長。在新增大量元素前,應用程式可以使用ensureCapacity 操作來增加 ArrayList 例項的容量。這可以減少遞增式再分配的數量。它繼承於AbstractList,實現了List, RandomAccess, Cloneable, java.io.Serializable這些介面。   在我們學資料結構的時候就知道了線性表的順序儲存,插入刪除元素的時間複雜度為O(n),求表長以及增加元素,取第 i 元素的時間複雜度為O(1)   ArrayList 繼承了AbstractList,實現了List。它是一個陣列佇列,提供了相關的新增、刪除、修改、遍歷等功能。   ArrayList 實現了RandmoAccess介面,即提供了隨機訪問功能。RandmoAccess是java中用來被List實現,為List提供快速訪問功能的。在ArrayList中,我們即可以通過元素的序號快速獲取元素物件,這就是快速隨機訪問。   ArrayList 實現了Cloneable介面,即覆蓋了函式clone(),能被克隆。   ArrayList 實現java.io.Serializable介面,這意味著ArrayList支援序列化能通過序列化去傳輸。   和Vector不同,ArrayList中的操作不是執行緒安全的!所以,建議在單執行緒中才使用ArrayList,而在多執行緒中可以選擇Vector或者CopyOnWriteArrayList。

ArrayList核心原始碼:

package java.util;

import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;


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

    /**
     * 預設初始容量大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空陣列(用於空例項)。
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

	 //用於預設大小空例項的共享空陣列例項。
      //我們把它從EMPTY_ELEMENTDATA陣列中區分出來,以知道在新增第一個元素時容量需要增加多少。
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 儲存ArrayList資料的陣列
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * ArrayList 所包含的元素個數
     */
    private int size;

    /**
     * 帶初始容量引數的建構函式。(使用者自己指定容量)
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
			//建立initialCapacity大小的陣列
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
			//建立空陣列
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     *預設建構函式,其預設初始容量為10
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 構造一個包含指定集合的元素的列表,按照它們由集合的迭代器返回的順序。
     */
    public ArrayList(Collection<? extends E> c) {
        //
		elementData = c.toArray();
		//如果指定集合元素個數不為0
        if ((size = elementData.length) != 0) {
            // c.toArray 可能返回的不是Object型別的陣列所以加上下面的語句用於判斷,
			//這裡用到了反射裡面的getClass()方法
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 用空陣列代替
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

    /**
     * 修改這個ArrayList例項的容量是列表的當前大小。 應用程式可以使用此操作來最小化ArrayList例項的儲存。 
     */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }
//下面是ArrayList的擴容機制
//ArrayList的擴容機制提高了效能,如果每次只擴充一個,
//那麼頻繁的插入會導致頻繁的拷貝,降低效能,而ArrayList的擴容機制避免了這種情況。
    /**
     * 如有必要,增加此ArrayList例項的容量,以確保它至少能容納元素的數量
     * @param   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);
        }
    }
   //得到最小擴容量
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
			  // 獲取預設的容量和傳入引數的較大值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
  //判斷是否需要擴容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
			//呼叫grow方法進行擴容,呼叫此方法代表已經開始擴容了
            grow(minCapacity);
    }

    /**
     * 要分配的最大陣列大小
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * ArrayList擴容的核心方法。
     */
    private void grow(int minCapacity) {
        // oldCapacity為舊容量,newCapacity為新容量
        int oldCapacity = elementData.length;
		//將oldCapacity 右移一位,其效果相當於oldCapacity /2,
		//我們知道位運算的速度遠遠快於整除運算,整句運算式的結果就是將新容量更新為舊容量的1.5倍,
        int newCapacity = oldCapacity + (oldCapacity >> 1);
		//然後檢查新容量是否大於最小需要容量,若還是小於最小需要容量,那麼就把最小需要容量當作陣列的新容量,
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
		//再檢查新容量是否超出了ArrayList所定義的最大容量,
		//若超出了,則呼叫hugeCapacity()來比較minCapacity和 MAX_ARRAY_SIZE,
		//如果minCapacity大於最大容量,則新容量則為ArrayList定義的最大容量,否則,新容量大小則為 minCapacity。 
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    //比較minCapacity和 MAX_ARRAY_SIZE
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

    /**
     *返回此列表中的元素數。 
     */
    public int size() {
        return size;
    }

    /**
     * 如果此列表不包含元素,則返回 true 。
     */
    public boolean isEmpty() {
		//注意=和==的區別
        return size == 0;
    }

    /**
     * 如果此列表包含指定的元素,則返回true 。
     */
    public boolean contains(Object o) {
		//indexOf()方法:返回此列表中指定元素的首次出現的索引,如果此列表不包含此元素,則為-1 
        return indexOf(o) >= 0;
    }

    /**
     *返回此列表中指定元素的首次出現的索引,如果此列表不包含此元素,則為-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++)
				//equals()方法比較
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

    /**
     * 返回此列表中指定元素的最後一次出現的索引,如果此列表不包含元素,則返回-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;
    }

    /**
     * 返回此ArrayList例項的淺拷貝。 (元素本身不被複制。) 
     */
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
			//Arrays.copyOf功能是實現陣列的複製,返回複製後的陣列。引數是被複制的陣列和複製的長度
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // 這不應該發生,因為我們是可以克隆的
            throw new InternalError(e);
        }
    }

    /**
     *以正確的順序(從第一個到最後一個元素)返回一個包含此列表中所有元素的陣列。 
     *返回的陣列將是“安全的”,因為該列表不保留對它的引用。 (換句話說,這個方法必須分配一個新的陣列)。
	 *因此,呼叫者可以自由地修改返回的陣列。 此方法充當基於陣列和基於集合的API之間的橋樑。
     */
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

    /**
     * 以正確的順序返回一個包含此列表中所有元素的陣列(從第一個到最後一個元素); 
	 *返回的陣列的執行時型別是指定陣列的執行時型別。 如果列表適合指定的陣列,則返回其中。 
	 *否則,將為指定陣列的執行時型別和此列表的大小分配一個新陣列。 
     *如果列表適用於指定的陣列,其餘空間(即陣列的列表數量多於此元素),則緊跟在集合結束後的陣列中的元素設定為null 。
	 *(這僅在呼叫者知道列表不包含任何空元素的情況下才能確定列表的長度。) 
     */
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // 新建一個執行時型別的陣列,但是ArrayList陣列的內容
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
			//呼叫System提供的arraycopy()方法實現陣列之間的複製
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

    // Positional Access Operations

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

    /**
     * 返回此列表中指定位置的元素。
     */
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

    /**
     * 用指定的元素替換此列表中指定位置的元素。 
     */
    public E set(int index, E element) {
		//對index進行界限檢查
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
		//返回原來在這個位置的元素
        return oldValue;
    }

    /**
     * 將指定的元素追加到此列表的末尾。 
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
		//這裡看到ArrayList新增元素的實質就相當於為陣列賦值
        elementData[size++] = e;
        return true;
    }

    /**
     * 在此列表中的指定位置插入指定的元素。 
	 *先呼叫 rangeCheckForAdd 對index進行界限檢查;然後呼叫 ensureCapacityInternal 方法保證capacity足夠大;
     *再將從index開始之後的所有成員後移一個位置;將element插入index位置;最後size加1。
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
		//arraycopy()這個實現陣列之間複製的方法一定要看一下,下面就用到了arraycopy()方法實現陣列自己複製自己
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

    /**
     * 刪除該列表中指定位置的元素。 將任何後續元素移動到左側(從其索引中減去一個元素)。 
     */
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
      //從列表中刪除的元素 
        return oldValue;
    }

    /**
     * 從列表中刪除指定元素的第一個出現(如果存在)。 如果列表不包含該元素,則它不會更改。
	 *返回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 remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

    /**
     * 從列表中刪除所有元素。 
     */
    public void clear() {
        modCount++;

        // 把陣列中所有的元素的值設為null
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

    /**
     * 按指定集合的Iterator返回的順序將指定集合中的所有元素追加到此列表的末尾。
     */
    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

    /**
     * 將指定集合中的所有元素插入到此列表中,從指定的位置開始。
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

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

    /**
     * 從此列表中刪除所有索引為fromIndex (含)和toIndex之間的元素。
	 *將任何後續元素移動到左側(減少其索引)。
     */
    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        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;
    }

    /**
     * 檢查給定的索引是否在範圍內。
     */
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    /**
     * add和addAll使用的rangeCheck的一個版本
     */
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    /**
     * 返回IndexOutOfBoundsException細節資訊
     */
    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }

    /**
     * 從此列表中刪除指定集合中包含的所有元素。 
     */
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
		//如果此列表被修改則返回true
        return batchRemove(c, false);
    }

    /**
     * 僅保留此列表中包含在指定集合中的元素。
	 *換句話說,從此列表中刪除其中不包含在指定集合中的所有元素。 
     */
    public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }


    /**
     * 從列表中的指定位置開始,返回列表中的元素(按正確順序)的列表迭代器。
	 *指定的索引表示初始呼叫將返回的第一個元素為next 。 初始呼叫previous將返回指定索引減1的元素。 
     *返回的列表迭代器是fail-fast 。 
     */
    public ListIterator<E> listIterator(int index) {
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("Index: "+index);
        return new ListItr(index);
    }

    /**
     *返回列表中的列表迭代器(按適當的順序)。 
     *返回的列表迭代器是fail-fast 。
     */
    public ListIterator<E> listIterator() {
        return new ListItr(0);
    }

    /**
     *以正確的順序返回該列表中的元素的迭代器。 
     *返回的迭代器是fail-fast 。 
     */
    public Iterator<E> iterator() {
        return new Itr();
    }

  
複製程式碼

ArrayList原始碼分析:

System.arraycopy()和Arrays.copyOf()方法

  通過上面原始碼我們發現這兩個實現陣列複製的方法被廣泛使用而且很多地方都特別巧妙。比如下面add(int index, E element)方法就很巧妙的用到了arraycopy()方法讓陣列自己複製自己實現讓index開始之後的所有成員後移一個位置:

	/**
     * 在此列表中的指定位置插入指定的元素。 
	 *先呼叫 rangeCheckForAdd 對index進行界限檢查;然後呼叫 ensureCapacityInternal 方法保證capacity足夠大;
     *再將從index開始之後的所有成員後移一個位置;將element插入index位置;最後size加1。
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
		//arraycopy()方法實現陣列自己複製自己
		//elementData:源陣列;index:源陣列中的起始位置;elementData:目標陣列;index + 1:目標陣列中的起始位置; size - index:要複製的陣列元素的數量;
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        elementData[index] = element;
        size++;
    }
複製程式碼

又如toArray()方法中用到了copyOf()方法


    /**
     *以正確的順序(從第一個到最後一個元素)返回一個包含此列表中所有元素的陣列。 
     *返回的陣列將是“安全的”,因為該列表不保留對它的引用。 (換句話說,這個方法必須分配一個新的陣列)。
	 *因此,呼叫者可以自由地修改返回的陣列。 此方法充當基於陣列和基於集合的API之間的橋樑。
     */
    public Object[] toArray() {
    //elementData:要複製的陣列;size:要複製的長度
        return Arrays.copyOf(elementData, size);
    }
複製程式碼
兩者聯絡與區別:

聯絡: 看兩者原始碼可以發現copyOf()內部呼叫了System.arraycopy()方法 區別: 1,arraycopy()需要目標陣列,將原陣列拷貝到你自己定義的陣列裡,而且可以選擇拷貝的起點和長度以及放入新陣列中的位置 2,copyOf()是系統自動在內部新建一個陣列,並返回該陣列。

ArrayList核心擴容技術

//下面是ArrayList的擴容機制
//ArrayList的擴容機制提高了效能,如果每次只擴充一個,
//那麼頻繁的插入會導致頻繁的拷貝,降低效能,而ArrayList的擴容機制避免了這種情況。
    /**
     * 如有必要,增加此ArrayList例項的容量,以確保它至少能容納元素的數量
     * @param   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);
        }
    }
   //得到最小擴容量
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
			  // 獲取預設的容量和傳入引數的較大值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
  //判斷是否需要擴容,上面兩個方法都要呼叫
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 如果說minCapacity也就是所需的最小容量大於儲存ArrayList資料的陣列的長度的話,就需要呼叫grow(minCapacity)方法擴容。
        //這個minCapacity到底為多少呢?舉個例子在新增元素(add)方法中這個minCapacity的大小就為現在陣列的長度加1
        if (minCapacity - elementData.length > 0)
			//呼叫grow方法進行擴容,呼叫此方法代表已經開始擴容了
            grow(minCapacity);
    }

複製程式碼
    /**
     * ArrayList擴容的核心方法。
     */
    private void grow(int minCapacity) {
       //elementData為儲存ArrayList資料的陣列
       ///elementData.length求陣列長度elementData.size是求陣列中的元素個數
        // oldCapacity為舊容量,newCapacity為新容量
        int oldCapacity = elementData.length;
		//將oldCapacity 右移一位,其效果相當於oldCapacity /2,
		//我們知道位運算的速度遠遠快於整除運算,整句運算式的結果就是將新容量更新為舊容量的1.5倍,
        int newCapacity = oldCapacity + (oldCapacity >> 1);
		//然後檢查新容量是否大於最小需要容量,若還是小於最小需要容量,那麼就把最小需要容量當作陣列的新容量,
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
		//再檢查新容量是否超出了ArrayList所定義的最大容量,
		//若超出了,則呼叫hugeCapacity()來比較minCapacity和 MAX_ARRAY_SIZE,
		//如果minCapacity大於最大容量,則新容量則為ArrayList定義的最大容量,否則,新容量大小則為 minCapacity。 
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
	
複製程式碼

  擴容機制程式碼已經做了詳細的解釋。另外值得注意的是大家很容易忽略的一個運算子:移位運算子   簡介:移位運算子就是在二進位制的基礎上對數字進行平移。按照平移的方向和填充數字的規則分為三種:<<(左移)>>(帶符號右移)>>>(無符號右移)。   作用對於大資料的2進位制運算,位移運算子比那些普通運算子的運算要快很多,因為程式僅僅移動一下而已,不去計算,這樣提高了效率,節省了資源   比如這裡:int newCapacity = oldCapacity + (oldCapacity >> 1); 右移一位相當於除2,右移n位相當於除以2的n次方。這裡oldCapacity 明顯右移了1位所以相當於oldCapacity /2。

另外需要注意的是:

  1. java中的length屬性是針對陣列說的,比如說你宣告瞭一個陣列,想知道這個陣列的長度則用到了length這個屬性.

  2. java中的length()方法是針對字 符串String說的,如果想看這個字串的長度則用到length()這個方法.

  3. .java中的size()方法是針對泛型集合說的,如果想看這個泛型有多少個元素,就呼叫此方法來檢視!

內部類

    (1)private class Itr implements Iterator<E>  
    (2)private class ListItr extends Itr implements ListIterator<E>  
    (3)private class SubList extends AbstractList<E> implements RandomAccess  
    (4)static final class ArrayListSpliterator<E> implements Spliterator<E>  
複製程式碼

  ArrayList有四個內部類,其中的Itr是實現了Iterator介面,同時重寫了裡面的hasNext()next()remove()等方法;其中的ListItr繼承Itr,實現了ListIterator介面,同時重寫了hasPrevious()nextIndex()previousIndex()previous()set(E e)add(E e)等方法,所以這也可以看出了Iterator和ListIterator的區別:ListIterator在Iterator的基礎上增加了新增物件,修改物件,逆向遍歷等方法,這些是Iterator不能實現的。具體可以參考http://blog.csdn.net/a597926661/article/details/7679765。其中的SubList繼承AbstractList,實現了RandmAccess介面,類內部實現了對子序列的增刪改查等方法,但它同時也充分利用了內部類的優點,就是共享ArrayList的全域性變數,例如檢查器變數modCount,陣列elementData等,所以SubList進行的增刪改查操作都是對ArrayList的陣列進行的,並沒有建立新的陣列。(內部類這裡參考了這位老兄的部落格http://blog.csdn.net/ljcitworld/article/details/52041836)

ArrayList經典Demo:

package list;
import java.util.ArrayList;
import java.util.Iterator;

public class ArrayListDemo {

    public static void main(String[] srgs){
         ArrayList<Integer> arrayList = new ArrayList<Integer>();

         System.out.printf("Before add:arrayList.size() = %d\n",arrayList.size());

         arrayList.add(1);
         arrayList.add(3);
         arrayList.add(5);
         arrayList.add(7);
         arrayList.add(9);
         System.out.printf("After add:arrayList.size() = %d\n",arrayList.size());

         System.out.println("Printing elements of arrayList");
         // 三種遍歷方式列印元素
         // 第一種:通過迭代器遍歷
         System.out.print("通過迭代器遍歷:");
         Iterator<Integer> it = arrayList.iterator();
         while(it.hasNext()){
             System.out.print(it.next() + " ");
         }
         System.out.println();

         // 第二種:通過索引值遍歷
         System.out.print("通過索引值遍歷:");
         for(int i = 0; i < arrayList.size(); i++){
             System.out.print(arrayList.get(i) + " ");
         }
         System.out.println();

         // 第三種:for迴圈遍歷
         System.out.print("for迴圈遍歷:");
         for(Integer number : arrayList){
             System.out.print(number + " ");
         }

         // toArray用法
         // 第一種方式(最常用)
         Integer[] integer = arrayList.toArray(new Integer[0]);

         // 第二種方式(容易理解)
         Integer[] integer1 = new Integer[arrayList.size()];
         arrayList.toArray(integer1);

         // 丟擲異常,java不支援向下轉型
         //Integer[] integer2 = new Integer[arrayList.size()];
         //integer2 = arrayList.toArray();
         System.out.println();

         // 在指定位置新增元素
         arrayList.add(2,2);
         // 刪除指定位置上的元素
         arrayList.remove(2);    
         // 刪除指定元素
         arrayList.remove((Object)3);
         // 判斷arrayList是否包含5
         System.out.println("ArrayList contains 5 is: " + arrayList.contains(5));

         // 清空ArrayList
         arrayList.clear();
         // 判斷ArrayList是否為空
         System.out.println("ArrayList is empty: " + arrayList.isEmpty());
    }
}
複製程式碼

歡迎關注我的微信公眾號(分享各種Java學習資源,面試題,以及企業級Java實戰專案回覆關鍵字免費領取):

微信公眾號

相關文章