ArrayList 應該是 Java 中最經常使用的集合類型了,以致於咱們說到集合就會天然而然的想到 ArrayList。不少同窗都沒有用過除了 ArrayList 以外的其餘集合,甚至於都已經忘了除了 ArrayList 以外的其餘集合,例如 LinkedList、Vector 等。java
那麼咱們平時只用 ArrayList 是否是正確呢,我既然知道了 LinkedList、Vector ,是否是也要用一下它呢,那麼什麼狀況下用呢。編程
Vector 是線程安全的集合類型,因此僅在多線程編程的時候才考慮是否用它,凡是爲了多線程設計,必然在性能上有所損失, Vector 只是在每一個方法上簡單的加上 synchronized
關鍵字,因此性能損失是確定的。數組
那麼如今就來比較一下 ArrayList 和 LinkedList,都說插入、刪除操做多的話用 LinkedList,插入操做多的話用 ArrayList,產生這種說法的大體依據以下。安全
ArrayList多線程
內部是用 Object 數組做爲存儲結構,數組是內存中連續的內存空間,而且繼承了 RandomAccess
接口,因此能夠實現元素的快速訪問。但若是不是在末尾插入元素的話,須要拷貝插入位置以後的元素。dom
LinkedList性能
內部本身實現的 Node 鏈表,每一個節點都指明瞭上一節點和下一節點, 因此不要求內存連續,而且插入數據只須要修改上一節點和下一節點的指針便可。可是訪問元素須要遍歷查找,因此查詢元素較慢。測試
真的是這樣嗎?下面我作了一系列的測試,來看一下真實的狀況。線程
分別從頭部、尾部和隨機位置插入數據,初始的列表長度分別爲 十、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 });
不管是1w、100w、1000w ,這五種方式的性能排名都是基本穩定的。原始 for 循環最優,forEach 方式最差。
不管是1w、100w、1000w ,這五種方式的性能排名都是基本穩定的。和 ArrayList 類似,都是原始 for 循環最優,forEach 方式最差,其餘三種方式相差不大。
觀察發現,迭代器方式、stream 方式、加強 for 循環這三種方式,ArrayList 的性能基本上都要優於 LinkedList,而最原始的 for 循環方式,直到列表長度達到了 1000w,仍然是 LinkedList 優於 ArrayList。
LinkedList 只有在頭部插入和刪除數據頻繁的時候纔有優點,可是能找到這種應用場景彷佛也不容易找到。如此看來,咱們在程序中用到列表就隨手 new 一個 ArrayList 倒也頗有道理。
不要吝惜你的「推薦」呦
本文中的測試使用 JMH 基準測試完成的,圖表是用 Excel 作的,若是須要源碼和 Excel 文件,請在公衆號內回覆關鍵詞 「jmh」
歡迎關注,不按期更新本系列和其餘文章
古時的風箏
,進入公衆號能夠加入交流羣