三、集合框架

3.1 接口

常見接口

  • Map 接口和 Collection 接口是所有集合框架的父接口;

  • Collection 接口的子接口包括:Set 接口、List 接口和Queue接口;

  • Map 接口的實現類主要有:HashMap、TreeMap、LinkedHashMap、Hashtable、ConcurrentHashMap 以及 Properties 等;

  • Set 接口的實現類主要有:HashSet、TreeSet、LinkedHashSet 等;

  • List 接口的實現類主要有:ArrayList、LinkedList、Stack 、Vector以及CopyOnWriteArrayList 等;

  • Queue接口的主要實現類有:ArrayDeque、ArrayBlockingQueue、LinkedBlockingQueue、PriorityQueue等;

 

List接口和Set接口的區別

  • List 元素是有序的,可以重複;Set 元素是無序的,不可以重複。

隊列、Set、Map 區別

  • List 有序列表

  • Set無序集合

  • Map鍵值對的集合

  • Queue隊列FlFO

ArrayList

  • 基於數組實現,無容量的限制。

  • 在執行插入元素時可能要擴容,在刪除元素時並不會減小數組的容量,在查找元素時要遍歷數組,對於非null的元素採取equals的方式尋找。

  • 是非線程安全的。

  • 注意點:

    • (1)ArrayList隨機元素時間複雜度O(1),插入刪除操作需大量移動元素,效率較低

    • (2)爲了節約內存,當新建容器爲空時,會共享Object[] EMPTY_ELEMENTDATA = {}和 Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}空數組

    • (3)容器底層採用數組存儲,每次擴容爲1.5倍

    • (4)ArrayList的實現中大量地調用了Arrays.copyof()和System.arraycopy()方法,其實Arrays.copyof()內部也是調用System.arraycopy()。System.arraycopy()爲Native方法

    • (5)兩個ToArray方法

  • Object[] toArray()方法。該方法有可能會拋出java.lang.ClassCastException異常

  • <T> T[] toArray(T[] a)方法。該方法可以直接將ArrayList轉換得到的Array進行整體向下轉型

    • (6)ArrayList可以存儲null值

    • (7)ArrayList每次修改(增加、刪除)容器時,都是修改自身的modCount;在生成迭代器時,迭代器會保存該modCount值,迭代器每次獲取元素時,會比較自身的modCount與ArrayList的modCount是否相等,來判斷容器是否已經被修改,如果被修改了則拋出異常(fast-fail機制)。

成員變量

/**
 * 序列號
 */
private static final long serialVersionUID = 8683452581122892189L;

/**
 * 默認容量
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 一個空數組
 * 當用戶指定該 ArrayList 容量爲 0 時,返回該空數組
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 一個空數組實例
 * - 當用戶沒有指定 ArrayList 的容量時(即調用無參構造函數),返回的是該數組==>剛創建一個 ArrayList 時,其內數據量爲 0。
 * - 當用戶第一次添加元素時,該數組將會擴容,變成默認容量爲 10(DEFAULT_CAPACITY) 的一個數組===>通過  ensureCapacityInternal() 實現
 * 它與 EMPTY_ELEMENTDATA 的區別就是:該數組是默認返回的,而後者是在用戶指定容量爲 0 時返回
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * ArrayList基於數組實現,用該數組保存數據, ArrayList 的容量就是該數組的長度
 * - 該值爲 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 時,當第一次添加元素進入 ArrayList 中時,數組將擴容值 DEFAULT_CAPACITY(10)
 */
transient Object[] elementData; // non-private to simplify nested class access

/**
 * ArrayList實際存儲的數據數量
 */
private int size;

構造方法

/**
 * 創建一個初試容量的、空的ArrayList
 * @param  initialCapacity  初始容量
 * @throws IllegalArgumentException 當初試容量值非法(小於0)時拋出
 */
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);
    }
}

添加 add(e)

/**
 *增加指定的元素到ArrayList的最後位置
 * @param e 要添加的元素
 * @return
 */
public boolean add(E e) {
    // 確定ArrayList的容量大小---嚴謹
    // 注意:size + 1,保證資源空間不被浪費,
    // ☆☆☆按當前情況,保證要存多少個元素,就只分配多少空間資源
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

即使初始化時指定大小:小於10個,添加元素時會調整大小,保證capacity不會少於10個。

/**
 * 私有方法:明確 ArrayList 的容量,提供給本類使用的方法
 * - 用於內部優化,保證空間資源不被浪費:尤其在 add() 方法添加時起效
 * @param minCapacity    指定的最小容量
 */
private void ensureCapacityInternal(int minCapacity) {
    // 若 elementData == {},則取 minCapacity 爲 默認容量和參數 minCapacity 之間的最大值
    // 注:ensureCapacity() 是提供給用戶使用的方法,在 ArrayList 的實現中並沒有使用
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    /**
     * - 當用戶沒有指定 ArrayList 的容量時(即調用無參構造函數),返回的是該數組            
         DEFAULTCAPACITY_EMPTY_ELEMENTDATA
         ==>剛創建一個ArrayList 時,其內數據量爲 0。
     * - 當用戶第一次添加元素時,該數組將會擴容,變成默認容量爲 10(DEFAULT_CAPACITY) 的一個數組 
         ===>通過  ensureCapacityInternal() 實現
     *   DEFAULTCAPACITY_EMPTY_ELEMENTDATA與 EMPTY_ELEMENTDATA 的區別就是:該數組是默認返回 
         的,而後者是在用戶指定容量爲 0 時返回
     */
        minCapacity= Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}
/**
 * 私有方法:明確 ArrayList 的容量
 * - 用於內部優化,保證空間資源不被浪費:尤其在 add() 方法添加時起效
 * @param minCapacity    指定的最小容量
 */
private void ensureExplicitCapacity(int minCapacity) {
    // 將“修改統計數”+1,該變量主要是用來實現fail-fast機制的
    modCount++;
    // 防止溢出代碼:確保指定的最小容量 > 數組緩衝區當前的長度
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

擴容

/**
 * 私有方法:擴容,以確保 ArrayList 至少能存儲 minCapacity 個元素
 * - 擴容計算:newCapacity = oldCapacity + (oldCapacity >> 1);  擴充當前容量的1.5倍
 * @param minCapacity    指定的最小容量
 */
private void grow(int minCapacity) {
    // 防止溢出代碼
    int oldCapacity = elementData.length;
    // 運算符 >> 是帶符號右移. 如 oldCapacity = 10,則 newCapacity = 10 + (10 >> 1) = 10 + 5 = 15
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)  // 若 newCapacity 依舊小於 minCapacity
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)   // 若 newCapacity 大於最大存儲容量,則進行大容量分配
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

Arrays.copyOf底層是System.arrayCopy

添加 add(index,e)

/**
 *
 *在這個ArrayList中的指定位置插入指定的元素,
 *  - 在指定位置插入新元素,原先在 index 位置的值往後移動一位
 * @param index 指定位置
 * @param element 指定元素
 * @throws IndexOutOfBoundsException
 */
public void add(int index, E element) {
    rangeCheckForAdd(index);//判斷角標是否越界
    //看上面的,size+1,保證資源空間不浪費,按當前情況,保證要存多少元素,就只分配多少空間資源
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //第一個是要複製的數組,第二個是從要複製的數組的第幾個開始,
    // 第三個是複製到那,四個是複製到的數組第幾個開始,最後一個是複製長度
    System.arraycopy(elementData, index, elementData, index + 1,
            size - index);
    elementData[index] = element;
    size++;
}
/**
 * 添加時檢查索引是否越界
 */
private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

刪除 remove(o)

/**
 * 移除list中指定的第一個元素(符合條件索引最低的)
 * 如果list中不包含這個元素,這個list不會改變
 * 如果包含這個元素,index 之後的所有元素依次左移一位
 * @param o 這個list中要被移除的元素
 * @return
 */
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;
}
/**
 * 快速刪除第 index 個元素
 * 和public E remove(int index)相比
 * 私有方法,跳過檢查,不返回被刪除的值
 * @param index 要刪除的腳標
 */
private void fastRemove(int index) {
    modCount++;//這個地方改變了modCount的值了
    int numMoved = size - index - 1;//移動的個數
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                numMoved);
    elementData[--size] = null; //將最後一個元素清除
}

public static void arraycopy(Object src, int srcPos, Object dest, int destPos,int length)
其中:src表示源數組,srcPos表示源數組要複製的起始位置,desc表示目標數組,length表示要複製的長度。

刪除 remove(index)

/**
 * 移除指定位置的元素
 * index 之後的所有元素依次左移一位
 * @param index 指定位置
 * @return 被移除的元素
 * @throws IndexOutOfBoundsException
 */
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;

    return oldValue;
}

 

發佈了87 篇原創文章 · 獲贊 18 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章