在面試的時候都會被問到集合相關的問題,好比:你能講講 ArrayList 和 LinkedList 的區別嗎?java
那麼我相信你確定可以答上來:ArrayList 是基於數組實現的, LinkedList 是基於鏈表實現的node
接下來面試官就會連環問了,那你能講講,它們都用在什麼場景下嗎?web
阿粉知道這種程度確定難不倒我們讀者的:由於 ArrayList 是基於數組實現的,因此在遍歷的時候, ArrayList 的效率是要比 LinkedList 高的, LinkedList 是基於鏈表實現的,因此在進行新增/刪除元素的時候, LinkedList 的效率是要比 ArrayList 高的面試
面試官:哦哦,好的,我大概瞭解了,我這邊沒有什麼想問的了,您回去等消息能夠嗎數組
???發生了什麼?微信
哈哈,上面模擬了一個面試場景,是想引出來這篇文章的主題:LinkedList 在新增/刪除元素時,效率比 ArrayList 高,這是真的嗎?app
我相信你也知道套路,通常這麼一問,那確定就不是真的了dom
放一張圖片,這是通過我測試以後的真實結果ide
由於微信不能放外鏈的緣故,能夠在公衆號後臺發送 「20200821」 獲取測試代碼測試
從圖中能夠看出來, LinkedList 在新增元素時,它的效率不必定比 ArrayList 高,這是要分狀況的
若是是從集合頭部位置新增元素的話,那確實是 LinkedList 的效率要比 ArrayList 高
可是若是是從集合中間位置或者是尾部位置新增元素, ArrayList 效率反而要比 LinkedList 效率要高
Excuse me ?居然和我之前學的不同?阿粉我學的淺,你別騙我
哈哈哈,爲何會這樣呢
這是由於 ArrayList 是基於數組實現的嘛,而數組是一塊連續的內存空間,因此在添加元素到數組頭部時,須要對頭部後面的數據進行復制重排,因此效率是蠻低的
可是 LinkedList 是基於鏈表實現的,在添加元素的時候,首先會經過循環查找到添加元素的位置,若是要添加的位置處於 List 前半段,那就從前向後找;若是位置在後半段,那就從後往前找,因此 LinkedList 添加元素到頭部是很是高效的(小聲 BB ,這我知道
哦,這你知道?看來基礎蠻不錯的嘛~
因此當 ArrayList 在添加元素到數組中間時,有一部分數據須要複製重排,效率就不是很高,那爲啥 LinkedList 比它還要低呢?這是由於 LinkedList 把元素添加到中間位置的時候,須要在添加以前先遍歷查找,這個查找的時間比較耗時
添加元素到尾部操做中, ArrayList 的效率要比 LinkedList 的還要高,這是爲啥嘞
由於 ArrayList 在添加的時候不須要什麼操做,直接插入就行了,因此效率蠻高的
可是 LinkedList 就不同了,對於 LinkedList 來講,也不須要查找啥的,直接插入就能夠了,可是須要 new 對象,還有變換指針指向對象呀,這些過程耗時加起來可就比 ArrayList 長了
它是有前提的,那就是 ArrayList 初始化容量是足夠的狀況下,纔有上述的特色,若是 ArrayList 涉及到動態擴容,那它的效率確定會下降
刪除元素和新增元素的原理是同樣的,因此刪除元素的操做和新增元素的操做耗時也是很相近
這裏就再也不贅述
測試結果很是明顯,對於 LinkedList 來講,若是使用 for 循環的話,效率特別低,可是 ArrayList 使用 for 循環去遍歷的話,就比較高
爲啥呢?
emmm ,得從源碼提及
先來看 ArrayList 的源碼吧,這個比較簡單
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
可以看到, ArrayList 實現了 List , RandomAccess , Cloneable 還有 Serializable 接口
你是否是對 RandomAccess 這個接口挺陌生的?這是個啥?
可是經過查閱源碼可以發現它也只是個空的接口罷了,那 ArrayList 爲啥還要去實現它嘞
由於 RandomAccess 接口是一個標誌接口,它標識着「只要實現該接口的 list 類,均可以實現快速隨機訪問」
實現快速隨機訪問?你能想到什麼?這不就是數組的特性嘛!能夠直接經過 index 來快速定位 & 讀取
那你是否是就能想到, ArrayList 是數組實現的,因此實現了 RandomAccess 接口, LinkedList 是用鏈表實現的,因此它沒有用 RandomAccess 接口實現吧?
beautiful ~就是這樣
咱瞅瞅 LinkedList 源碼
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
果真,跟我們設想的同樣,沒有實現 RandomAccess 接口
那爲啥 LinkedList 接口使用 for 循環去遍歷的時候,慢的不行呢?
我們瞅瞅 LinkedList 在 get 元素時,都幹了點兒啥
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
在 get 方法中,主要調用了 node() 方法,由於 LinkedList 是雙向鏈表,因此 if (index < (size >> 1)) 在判斷 i 是在前半段仍是後半段,若是是前半段就正序遍歷,若是是在後半段那就倒序遍歷,那麼爲何使用 for 循環遍歷 LinkedList 時,會這麼慢?(好像離真相愈來愈近了
緣由就在兩個 for 循環之中,以第一個 for 循環爲例
get(0) :直接拿到了node0 地址,而後拿到 node0 的數據
get(1) :先拿到 node0 地址,而後 i < index ,開始拿 node1 的地址,符合條件,而後去拿 node1 的數據
get(2) :先拿到 node0 的地址,而後 i < index ,拿到 node1 的地址, i < index ,繼續向下走,拿到 node2 的地址,符合條件,獲取 node2 的數據
發現問題了嘛?我就是想要 2 的數據, LinkedList 在遍歷時,將 0 和 1 也給遍歷了,若是數據量很是大的話,那效率可不就唰唰的下來了嘛
那到如今,我們也就很是明確了,若是是要遍歷 ArrayList 的話,最好是用 for 循環去作,若是要遍歷 LinkedList 的話,最好是用迭代器去作
我猜你必定會說,阿粉啊,那若是對方就給我傳過來了一個 list ,我不知道它是 ArrayList 仍是 LinkedList 呀?我該怎麼辦呢
還記得 ArrayList 和 LinkedList 有什麼不一樣嗎?是否是 ArrayList 實現了 RandomAccess 接口,可是 LinkedList 沒有實現,因此能夠從這點去着手解決
暖暖的阿粉在這裏給個簡單的小 demo ,能夠參考下:
public class ListTest {
public static void main(String[] args) {
List<String> arrayList = new ArrayList<String>();
arrayList.add("aaa");
arrayList.add("bbb");
isUseIterator(arrayList);
List<String> linkedList = new LinkedList<String>();
linkedList.add("ccc");
linkedList.add("ddd");
isUseIterator(linkedList);
}
public static void isUseIterator(List list){
if (list instanceof RandomAccess){
System.out.println("實現了 RandomAccess 接口,使用 for 循環遍歷");
for (int i = 0 ; i < list.size(); i++ ){
System.out.println(list.get(i));
}
}else{
System.out.println("沒有實現 RandomAccess 接口,使用迭代器遍歷");
Iterator it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
因此,乖,下次面試官再問你 LinkedList 在新增/刪除元素時,效率比 ArrayList 高嗎,不要再傻傻的回答是了,拿阿粉這篇文章和他扯皮,保證沒問題