【知識總結】ArrayList

ArrayList集合底層數據結構

  • List 接口的可調節大小的數組實現。
  • 特色:增刪慢,查詢快。

ArrayList實現的三個接口

它實現了三個接口,相同點都是空接口,做爲一個標記出現。java

  • Serializable標識接口

類的序列化由實現這個接口的類啓動。不實現此接口的類不會使用任何狀態序列化和反序列化。數組

序列化:將對象的數據寫入到文件安全

反序列化:將文件中的對象的數據讀取出來數據結構

  • Cloneable標識接口

只有實現這個 「可克隆」 接口,而後在類中重寫 Object 中的 clone() 方法,後面經過類調用 clone 方法才能克隆成功。若是不實現這個接口,則會拋出 克隆不被支持 的異常。併發

  • RandomAccess標識接口

只要實現了這個接口,就能支持快速隨機訪問。dom

  1. ArrayList 實現了 RandomAccess 接口,使用 for 循環經過索引遍歷。
  2. LinkedList 沒有實現這個接口,默認使用 iterator 進行遍歷。

經過測試,他們這樣各自的實現是最高效的,因此實現 RandomAccess 接口也須要根據實際場景需求來進行。工具

ArrayList構造方法

構造方法 描述
ArrayList() 構造一個初始容量爲 10 的空列表
ArrayList(int initialCapacity) 構造一個指定初始容量的空列表
ArrayList(Collection<? extends E> c) 構造一個包含指定集合元素的列表(參數就是一個集合)

各個方法的時間複雜度

方法 時間複雜度
add(E e) 添加元素到末尾,平均時間複雜度爲O(1)
add(int index, E e) 添加元素到指定位置,平均時間複雜度爲O(n)
get(int index) 獲取指定索引位置的元素,時間複雜度爲O(1)
remove(int index) 刪除指定索引位置的元素,時間複雜度爲O(n)
remove(Object o) 刪除指定元素值的元素,時間複雜度爲O(n)

Add方法源碼分析

public boolean add(E e) {
    //調用方法對內部容量進行檢查
    ensureCapacityInternal(size + 1); 
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    //若是 elementData 數組爲空
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //若是容量不足10,擴容至默認容量10(第一次擴容的參數,此時數組還爲null)
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);    //將上面計算出的容量傳遞,繼續檢查
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;        //記錄修改的次數++ (主要用於迭代器中)
    
    //當前最小容量 - 數組長度 > 0(判斷是否溢出)
    if (minCapacity - elementData.length > 0)
        //將第一次計算出來的容量傳給 「核心擴容方法」
        grow(minCapacity);    
}
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;                //記錄舊的數組容量
    int newCapacity = oldCapacity + (oldCapacity >> 1);        //新容量擴容1.5倍
    
    //若是新容量小於最小容量,則將最小容量的值賦給 新容量(若是是第一次調用add方法必然小於)
    if (newCapacity - minCapacity < 0)                    
        newCapacity = minCapacity;
    
    //若是newCapacity大於數組最大容量(默認是int類型最大值)
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);    //建立超大數組,Integer.MAX_VALUE
    
    //建立一個新數組,將新數組的地址賦值給elementData
    elementData = Arrays.copyOf(elementData, newCapacity);
}
  • 若是容量不足10,第一次擴容至10,之後每次都是原容量的 1.5 倍。

ArrayList 頻繁擴容致使添加性能降低的解決辦法

建立集合的時候指定足夠大的容量。源碼分析

ArrayList線程安全嗎?

ArrayList 不是線程安全的,例如當咱們 add 操做時,elementData[size++] 這個操做並非原子操做。性能

ArrayList 中 elementData 爲何使用 transient 修飾?

transient 關鍵字修飾的對象不會被序列化。由於 elementData 是 ArrayList 的數據域,因爲 ArrayList 是基於動態數組實現的, elementData 的容量一般大於實際存儲元素的容量,因此只需發送有實際值的數組元素便可。測試

ArrayList 和 LinkedList 的區別

數據結構:ArrayList底層是數組(內存裏是連續的空間),LinkedList底層鏈表(內存空間不連續)。

訪問效率:ArrayListLinkedList 在隨機訪問的時候效率要高,由於 LinkedList 線性的數據存儲方式,因此須要移動指針從前日後依次查找。

增刪效率:LinkedList 要比 ArrayList 效率要高,由於 ArrayList 增刪操做要影響數組內的其餘數據的下標。LinkedList只須要改變先後的指針就能夠了。

ArrayList和Vector的區別

ArrayList 和 Vector的底層都是基於數組實現的動態擴容。他們的區別在於:

線程安全:Vector 使用了 Synchronized 來實現線程同步,而 ArrayList 是線程不安全的。

性能:ArrayList 在性能方面優於 Vector。

擴容:Vector 擴容每次擴容 2 倍,而 ArrayList 擴容 1.5 倍。

ArrayList 相應的線程安全容器

ArrayList和LinkedList的區別和原理

  • 使用 Vector,方法上添加了 Synchronized 來實現線程同步,但已經不推薦了。
  • 使用集合工具類的 Collections.synchronizedList() 方法。
  • 使用 CopyOnWriteArrayList 類建立集合。

CopyOnWriteArray介紹

CopyOnWriteArray 容器是一個 寫時複製 的容器。

往一個容器添加元素的時候,不直接往當前容器的 Object[] 添加,而是先將當前容器 Object[] 進行復制,複製出一個新的容器。而後向新的容器裏添加元素,添加完元素以後,再將原容器的引用指向新的容器。

這樣作的好處是,能夠對 CopyOnWrite 容器進行併發的讀,而不須要加鎖,由於當前容器不會添加任何元素。因此 CopyOnWrite 容器是一種 「讀寫分離」 的思想,讀和寫是不一樣的容器。

缺點是每次寫入都要複製一個新的數組,會形成內存浪費,垃圾回收頻繁等,適合讀多寫少的場景。

相關文章
相關標籤/搜索