1. 程式人生 > >基準測試了 ArrayList 和 LinkedList ,發現我們一直用 ArrayList 也是沒什麼問題的

基準測試了 ArrayList 和 LinkedList ,發現我們一直用 ArrayList 也是沒什麼問題的

ArrayList 應該是 Java 中最常用的集合型別了,以至於我們說到集合就會自然而然的想到 ArrayList。很多同學都沒有用過除了 ArrayList 之外的其他集合,甚至於都已經忘了除了 ArrayList 之外的其他集合,例如 LinkedList、Vector 等。

那麼我們平時只用 ArrayList 是不是正確呢,我既然知道了 LinkedList、Vector ,是不是也要用一下它呢,那麼什麼情況下用呢。

Vector 是執行緒安全的集合型別,所以僅在多執行緒程式設計的時候才考慮是否用它,凡是為了多執行緒設計,必然在效能上有所損失, Vector 只是在每個方法上簡單的加上 synchronized

關鍵字,所以效能損失是肯定的。

那麼現在就來比較一下 ArrayList 和 LinkedList,都說插入、刪除操作多的話用 LinkedList,插入操作多的話用 ArrayList,產生這種說法的大致依據如下。

ArrayList

內部是用 Object 陣列作為儲存結構,陣列是記憶體中連續的記憶體空間,並且繼承了 RandomAccess介面,所以可以實現元素的快速訪問。但如果不是在末尾插入元素的話,需要拷貝插入位置之後的元素。

LinkedList

內部自己實現的 Node 連結串列,每個節點都指明瞭上一節點和下一節點, 所以不要求記憶體連續,並且插入資料只需要修改上一節點和下一節點的指標即可。但是訪問元素需要遍歷查詢,所以查詢元素較慢。

真的是這樣嗎?下面我做了一系列的測試,來看一下真實的情況。

插入資料

分別從頭部、尾部和隨機位置插入資料,初始的列表長度分別為 10、1w、10w 來測試幾種插入資料的效能,以平均耗時為依據。得到的結果如下:


頭部插入資料,ArrayList 耗時明顯大於 LinkedList ,得出結論頭部插入資料 LinkedList 效能優於 ArrayList,因為在頭部位置插入資料時,ArrayList 要拷貝插入位置之後的資料,往後移動一個位置。


尾部插入資料,ArrayList 耗時小於 LinkedList,得出結論:尾部插入資料,ArrayList 效能優於 LinkedList。


隨機位置是用 Random 產生的,上限是當前列表長度。可見隨機位置插入資料,ArrayList 耗時小於 LinkedList ,得出結論:多次隨機位置插入資料,平均效能 ArrayList 優於 LinkedList,注意,這裡說的是多次隨機插入求的平均值。

插入資料得出以下結論
插入位置 初始列表長度10 初始列表長度1w 初始列表長度10w
頭部 LinkedList 優於 ArrayList LinkedList 優於 ArrayList LinkedList 優於 ArrayList
尾部 ArrayList 優於 LinkedList ArrayList 優於 LinkedList ArrayList 優於 LinkedList
多次隨機位置 ArrayList 優於 LinkedList ArrayList 優於 LinkedList ArrayList 優於 LinkedList

獲取資料

分別從頭部、尾部和隨機位置獲取資料,初始的列表長度分別為1000、10w、100w、1000w 來測試幾種獲取資料的效能,以吞吐量為判斷依據,吞吐量單位是 每毫秒內運算元。得到的結果如下:

頭部獲取資料,ArrayList 吞吐量大於 LinkedList,得出結論頭部獲取資料,ArrayList 效能優於 LinkedList


尾部獲取資料,ArrayList 吞吐量大於 LinkedList,得出結論尾部獲取資料,ArrayList 效能優於 LinkedList


多次隨機位置獲取資料,ArrayList 吞吐量大於 LinkedList,得出結論多次隨機位置獲取資料,ArrayList 效能優於 LinkedList

綜上,獲取資料操作的結論如下
獲取位置 列表長度1000 列表長度10w 列表長度100w 列表長度1000w
頭部 ArrayList 優於 LinkedList ArrayList 優於 LinkedList ArrayList 優於 LinkedList ArrayList 優於 LinkedList
尾部 ArrayList 優於 LinkedList ArrayList 優於 LinkedList ArrayList 優於 LinkedList ArrayList 優於 LinkedList
多次隨機位置 ArrayList 優於 LinkedList ArrayList 優於 LinkedList ArrayList 優於 LinkedList ArrayList 優於 LinkedList

這是毋庸置疑的,實現了隨機訪問並且內部以陣列形式儲存資料的 ArrayList 擁有明顯的優勢。

刪除資料

分別從頭部、尾部和隨機位置獲取資料,初始的列表長度分別為1w、100w、1000w 來測試幾種刪除資料的效能,以吞吐量為判斷依據,吞吐量單位是 每毫秒內運算元。得到的結果如下:


頭部刪除資料,LinkedList 吞吐量大於 ArrayList,得出結論:頭部刪除資料,LinkedList 效能優於 ArrayList


尾部刪除資料,ArrayList 吞吐量大於 LinkedList,得出結論:尾部刪除資料,ArrayList 效能優於 LinkedList


多次隨機位置刪除資料,ArrayList 吞吐量大於 LinkedList,得出結論:尾部刪除資料,ArrayList 效能優於 LinkedList

綜上,刪除資料操作的結論如下
獲取位置 列表長度100w 列表長度1000w
頭部 LinkedList 優於 ArrayList LinkedList 優於 ArrayList
尾部 ArrayList 優於 LinkedList ArrayList 優於 LinkedList
多次隨機位置 ArrayList 優於 LinkedList ArrayList 優於 LinkedList

遍歷資料

分別比較了 5 種遍歷方式在1000、100w、1000w 長度的列表下的效能狀況。

遍歷方式一,原始形式的 for 迴圈

for(int i = 0;i<x;i++){
  //do something
}

遍歷方式二,增強形式的 for 迴圈

for(Integer i : arrayList){
  // do something
}

遍歷方式三,迭代器方式

Iterator iterator = arrayList.iterator();
while (iterator.hasNext()){
   // do something
}

遍歷方式四,forEach 方式

arrayList.forEach(integer -> {
   // do something
});

遍歷方式五,stream 方式

正常來講,stream 後面再接 forEach 是不太正確的測試方式,一般 stream 都會配合 map、filter 等操作來進行篩選、計算等。這裡為了方面測試,姑且先這個寫了。

arrayList.stream().forEach(integer -> {
   // do something
});
ArrayList 這 5 種方式遍歷的效能



無論是1w、100w、1000w ,這五種方式的效能排名都是基本穩定的。原始 for 迴圈最優,forEach 方式最差。

LinkedList 這 5 種方式遍歷的效能



無論是1w、100w、1000w ,這五種方式的效能排名都是基本穩定的。和 ArrayList 相似,都是原始 for 迴圈最優,forEach 方式最差,其他三種方式相差不大。

比較這五種方式下 ArrayList 和 LinkedList 的效能



觀察發現,迭代器方式、stream 方式、增強 for 迴圈這三種方式,ArrayList 的效能基本上都要優於 LinkedList,而最原始的 for 迴圈方式,直到列表長度達到了 1000w,仍然是 LinkedList 優於 ArrayList。

LinkedList 只有在頭部插入和刪除資料頻繁的時候才有優勢,但是能找到這種應用場景似乎也不容易找到。如此看來,我們在程式中用到列表就隨手 new 一個 ArrayList 倒也很有道理。

不要吝惜你的「推薦」呦

本文中的測試使用 JMH 基準測試完成的,圖表是用 Excel 做的,如果需要原始碼和 Excel 檔案,請在公眾號內回覆關鍵詞 「jmh」

歡迎關注,不定期更新本系列和其他文章
古時的風箏 ,進入公眾號可以加入交流群