Java中常見數據結構List之ArrayList


這里主要包含ArrayList和LinkedList.
關於Java中的集合內容, 感覺都已經被寫爛了, 我這里主要是做個復習, 再從扒下源代碼, 盡量用最直白的語言把里面的核心內容記錄下來。僅此而已。

首先放一個Collection下的UML圖:(此圖是idea通過diagram功能生成的, 基於JDK7)

一、ArrayList
1, for-each原理:
0、 在編譯的時候編譯器會自動將對for這個關鍵字的使用轉化為對目標的迭代器的使用,這就是foreach循環的原理
1、ArrayList之所以能使用foreach循環遍歷,是因為ArrayList所有的List都是Collection的子接口,而Collection是Iterable的子接口,ArrayList的父類AbstractList正確地實現了Iterable接口的iterator方法。之前我自己寫的ArrayList用foreach循環直接報空指針異常是因為我自己寫的ArrayList並沒有實現Iterable接口
2、任何一個集合,無論是JDK提供的還是自己寫的,只要想使用foreach循環遍歷,就必須正確地實現Iterable接口。

2, 集合中的 fail-fast iterator:
Iterator 是工作在一個獨立的線程中,並且擁有一個 mutex 鎖。 Iterator 被創建之后會建立一個指向原來對象的單鏈索引表,當原來的對象數量發生變化時,這個索引表的內容不會同步改變,所以當索引指針往后移動的時候就找不到要迭代的對象,所以按照 fail-fast 原則 Iterator 會馬上拋出 java.util.ConcurrentModificationException 異常。

當使用foreach遍歷一個list元素時, 因為foreach底層實現是使用iteator中的hasNext, next等, 源碼中next執行時會checkForComdification:

3, ArrayList初始值大小是10, 每次擴容是增加2倍。優缺點是:

//默認的構造函數, 當然還有構造函數是可以指定大小的
public ArrayList() {
    this(10);
}

//jdk7中的擴容代碼, 我看了jdk6中是擴容1.5倍的, 這里有點差別
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = 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);
}

ArrayList的優點:
1、ArrayList底層以數組實現,是一種隨機訪問模式,再加上它實現了RandomAccess接口,因此查找也就是get的時候非常快
2、ArrayList在順序添加一個元素的時候非常方便,只是往數組里面添加了一個元素而已
ArrayList的缺點:
1、刪除元素的時候,涉及到一次元素復制,如果要復制的元素很多,那么就會比較耗費性能
2、插入元素的時候,涉及到一次元素復制,如果要復制的元素很多,那么就會比較耗費性能
因此,ArrayList比較適合順序添加、隨機訪問的場景。

4,細節: 為什么ArrayList的elementData是用transient修飾的?
ArrayList實現了Serializable接口,這意味着ArrayList是可以被序列化的,用transient修飾elementData意味着我不希望elementData數組被序列化。這是為什么?因為序列化ArrayList的時候,ArrayList里面的elementData未必是滿的,比方說elementData有10的大小,但是我只用了其中的3個,那么是否有必要序列化整個elementData呢?顯然沒有這個必要,因此ArrayList中重寫了writeObject方法:

private transient Object[] elementData;
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 array length
    s.writeInt(elementData.length);

    // 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();
    }

}

每次序列化的時候調用這個方法,先調用defaultWriteObject()方法序列化ArrayList中的非transient元素,elementData不去序列化它,然后遍歷elementData,只序列化那些有的元素,這樣:
1、加快了序列化的速度
2、減小了序列化之后的文件大小


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM