1. 程式人生 > 實用技巧 >ArrayList 與LinkedList 原始碼分析,效率比較

ArrayList 與LinkedList 原始碼分析,效率比較

ArrayList 與LinkedList 原始碼分析,比較

ArrayList , LinkedList都是 List介面的實現類, ArrayList 底層是一個Object陣列, LinkedList是一個雙向連結串列,他們都是執行緒不安全的

ArrayList

先看看變數吧:
elementData : ArrayList資料的存放位置, 一目瞭然,是一個Object陣列, 修飾符 transtient 的作用是使得該變數不能再被序列化
存放資料的位置
DEFAULT_CAPACITY : ArrayList的預設容量,初始化時若沒有顯示指定容量, 預設為 10 , 在 ensureCapacityInternal 方法中設定預設容量

預設容量
size : 當前該ArrayList的大小, 實際儲存的資料量,注意與ellmentData陣列的長度區別 , 是不一樣的
size
modCount : 這是ArrayList的 父類AbstractList類中定義的變數,表示結構修改的次數, 當併發時, 若modCount不一致,觸發快速失敗

然後我們知道,java陣列建立之後長度是固定的, 一直往數組裡新增元素,陣列下標會越界,這就需要對陣列進行擴容

擴容時機: list的容量(Object陣列的長度 ) < 需要儲存的資料量。
下圖程式碼中, minCapacity 指的是當前需要儲存的資料量(size + 1),
時機
擴容大小: 1.5倍,

如果新的陣列大小大於 該ArrayList最大容量時,就更新為 MAX_ARRAY_SIZE。 下圖關鍵程式碼 :
newCapacity = oldCapacity + (oldCapacity >> 1) , >> : 位運算,往右移動一位,
擴容方法
擴容方法: Arrays.copyOf() , 重新申請記憶體,再賦值給elementData,該方法使用 System類的arraycopy() (這是一個native方法)移動資料, 操作記憶體,整體移動資料,效率高,包括插入,刪除操作的移動資料也是使用該arraycopy方法

LinkedList

底層是一個雙向連結串列,節點 結構如下:next 指向下一個節點, prev指向前一個,

節點結構
維護了兩個引用,first 指向連結串列的第一個節點, last指向最後一個, 因為是連結串列 ,所以不存在擴容,直接插入即可節點結構
node(int index) 判斷 index 靠近左邊還是右邊從更近的地方開始移動

ArrayList, LinkedList 查詢效率比較:
ArrayList 是 Object陣列實現的,可以直接通過下標隨機查詢, 時間複雜度 是O(1),
LinkedList 底層是雙向連結串列, LinkedList 需要從 頭或尾節點 移動到指定的節點,時間複雜度為 O (n), 所以 ArrayList的查詢效率高

插入,刪除效率比較:
具體需要分情況討論: (請注意,這裡的效率比較,都預設資料量很大的情況)

操作尾部, ArrayList, LinkedList 效率差不多,都可以直接插入

操作首部,LinkedList 效率高 ,可以直接插入,主要時間開銷是,生成節點的時間, ArrayList (Object陣列)需要移動操作位置後的元素,擴容也需要時間

操作非首尾部元素時, ArrayList效率高 , 因為 ArrayList 雖然需要移動資料,但,使用的是 System.arraycopy() (native方法)操作記憶體,整體移動,效率高, LinkedList 需要從 頭或尾節點 移動到指定的節點再操作,效率低, 並且資料量越大,差距越明顯

for, 增加for(forEach),迭代器遍歷效率比較:
ArrayList: 三種遍歷方式的效率都差不多 ,時間複雜度都是 O(N)

LinkedList : 迭代器 、forEach遍歷方式,效率比普通 for迴圈效率高很多,因為普通for,每次獲取元素(get方法)都需要從頭部或尾部,移動到指定的元素,這會產生 O(N)的時間複雜度,所以說,for迴圈遍歷,時間複雜度達到了 O(N * N),而 迭代器、forEach方式遍歷時間複雜度為O(N)