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 種方式遍歷的性能
LinkedList 這 5 種方式遍歷的性能
比較這五種方式下 ArrayList 和 LinkedList 的性能
LinkedList 只有在頭部插入和刪除數據頻繁的時候才有優勢,但是能找到這種應用場景似乎也不容易找到。如此看來,我們在程序中用到列表就隨手 new 一個 ArrayList 倒也很有道理。
不要吝惜你的「推薦」呦
本文中的測試使用 JMH 基准測試完成的,圖表是用 Excel 做的,如果需要源碼和 Excel 文件,請在公眾號內回復關鍵詞 「jmh」
歡迎關注,不定期更新本系列和其他文章
古時的風箏
,進入公眾號可以加入交流群