逼着面試官問了我ArrayList和LinkedList的區別,他對我徹底服了

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 和 LinkedList 有什麼區別,是面試官非常喜歡問的一個問題。可能大部分小夥伴和我一樣,能回答出“ArrayList 是基於數組實現的,LinkedList 是基於雙向鏈表實現的。”"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於這一點,我之前的文章裏也提到過了。但說實話,這樣蒼白的回答並不能令面試官感到滿意,他還想知道的更多。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那假如小夥伴們繼續做出下面這樣的回答:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"“ArrayList 在新增和刪除元素時,因爲涉及到數組複製,所以效率比 LinkedList 低,而在遍歷的時候,ArrayList 的效率要高於 LinkedList。”"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"面試官會感到滿意嗎?我只能說,如果面試官比較仁慈的話,他可能會讓我們回答下一個問題;否則的話,他會讓我們回家等通知,這一等,可能意味着杳無音訊了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲什麼會這樣呢?爲什麼爲什麼?回答的不對嗎?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"暴躁的小夥伴請喝口奶茶冷靜一下。冷靜下來後,請隨我來,讓我們一起肩並肩、手拉手地深入地研究一下 ArrayList 和 LinkedList 的數據結構、實現原理以及源碼,可能神祕的面紗就揭開了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"01、ArrayList 是如何實現的?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f5/f5e4865f3e9a2031f605d9f3237a1520.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 實現了 List 接口,繼承了 AbstractList 抽象類,底層是基於數組實現的,並且實現了動態擴容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ArrayList extends AbstractList\n implements List, RandomAccess, Cloneable, java.io.Serializable\n{\n private static final int DEFAULT_CAPACITY = 10;\n transient Object[] elementData;\n private int size;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 還實現了 RandomAccess 接口,這是一個標記接口:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface RandomAccess {\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內部是空的,標記“實現了這個接口的類支持快速(通常是固定時間)隨機訪問”。快速隨機訪問是什麼意思呢?就是說不需要遍歷,就可以通過下標(索引)直接訪問到內存地址。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public E get(int index) {\n Objects.checkIndex(index, size);\n return elementData(index);\n}\nE elementData(int index) {\n return (E) elementData[index];\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 還實現了 Cloneable 接口,這表明 ArrayList 是支持拷貝的。ArrayList 內部的確也重寫了 Object 類的 "},{"type":"codeinline","content":[{"type":"text","text":"clone()"}]},{"type":"text","text":" 方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public Object clone() {\n try {\n ArrayList> v = (ArrayList>) super.clone();\n v.elementData = Arrays.copyOf(elementData, size);\n v.modCount = 0;\n return v;\n } catch (CloneNotSupportedException e) {\n // this shouldn't happen, since we are Cloneable\n throw new InternalError(e);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 還實現了 Serializable 接口,同樣是一個標記接口:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface Serializable {\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內部也是空的,標記“實現了這個接口的類支持序列化”。序列化是什麼意思呢?Java 的序列化是指,將對象轉換成以字節序列的形式來表示,這些字節序中包含了對象的字段和方法。序列化後的對象可以被寫到數據庫、寫到文件,也可用於網絡傳輸。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"眼睛雪亮的小夥伴可能會注意到,ArrayList 中的關鍵字段 elementData 使用了 transient 關鍵字修飾,這個關鍵字的作用是,讓它修飾的字段不被序列化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這不前後矛盾嗎?一個類既然實現了 Serilizable 接口,肯定是想要被序列化的,對吧?那爲什麼保存關鍵數據的 elementData 又不想被序列化呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這還得從 “ArrayList 是基於數組實現的”開始說起。大家都知道,數組是定長的,就是說,數組一旦聲明瞭,長度(容量)就是固定的,不能像某些東西一樣伸縮自如。這就很麻煩,數組一旦裝滿了,就不能添加新的元素進來了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 不想像數組這樣活着,它想能屈能伸,所以它實現了動態擴容。一旦在添加元素的時候,發現容量用滿了 "},{"type":"codeinline","content":[{"type":"text","text":"s == elementData.length"}]},{"type":"text","text":",就按照原來數組的 1.5 倍("},{"type":"codeinline","content":[{"type":"text","text":"oldCapacity >> 1"}]},{"type":"text","text":")進行擴容。擴容之後,再將原有的數組複製到新分配的內存地址上 "},{"type":"codeinline","content":[{"type":"text","text":"Arrays.copyOf(elementData, newCapacity)"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void add(E e, Object[] elementData, int s) {\n if (s == elementData.length)\n elementData = grow();\n elementData[s] = e;\n size = s + 1;\n}\n\nprivate Object[] grow() {\n return grow(size + 1);\n}\n\nprivate Object[] grow(int minCapacity) {\n int oldCapacity = elementData.length;\n if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {\n int newCapacity = ArraysSupport.newLength(oldCapacity,\n minCapacity - oldCapacity, /* minimum growth */\n oldCapacity >> 1 /* preferred growth */);\n return elementData = Arrays.copyOf(elementData, newCapacity);\n } else {\n return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"動態擴容意味着什麼?大傢伙想一下。嗯,還是我來告訴大家答案吧,有點迫不及待。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"意味着數組的實際大小可能永遠無法被填滿的,總有多餘出來空置的內存空間。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如說,默認的數組大小是 10,當添加第 11 個元素的時候,數組的長度擴容了 1.5 倍,也就是 15,意味着還有 4 個內存空間是閒置的,對吧?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"序列化的時候,如果把整個數組都序列化的話,是不是就多序列化了 4 個內存空間。當存儲的元素數量非常非常多的時候,閒置的空間就非常非常大,序列化耗費的時間就會非常非常多。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"於是,ArrayList 做了一個愉快而又聰明的決定,內部提供了兩個私有方法 writeObject 和 readObject 來完成序列化和反序列化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void writeObject(java.io.ObjectOutputStream s)\n throws java.io.IOException {\n // Write out element count, and any hidden stuff\n int expectedModCount = modCount;\n s.defaultWriteObject();\n\n // Write out size as capacity for behavioral compatibility with clone()\n s.writeInt(size);\n\n // Write out all elements in the proper order.\n for (int i=0; i\n extends AbstractSequentialList\n implements List, Deque, Cloneable, java.io.Serializable\n{\n transient int size = 0;\n transient Node first;\n transient Node last;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" LinkedList 內部定義了一個 Node 節點,它包含 3 個部分:元素內容 item,前引用 prev 和後引用 next。代碼如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private static class Node {\n E item;\n LinkedList.Node next;\n LinkedList.Node prev;\n\n Node(LinkedList.Node prev, E element, LinkedList.Node next) {\n this.item = element;\n this.next = next;\n this.prev = prev;\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList 還實現了 Cloneable 接口,這表明 LinkedList 是支持拷貝的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList 還實現了 Serializable 接口,這表明 LinkedList 是支持序列化的。眼睛雪亮的小夥伴可能又注意到了,LinkedList 中的關鍵字段 size、first、last 都使用了 transient 關鍵字修飾,這不又矛盾了嗎?到底是想序列化還是不想序列化?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"答案是 LinkedList 想按照自己的方式序列化,來看它自己實現的 "},{"type":"codeinline","content":[{"type":"text","text":"writeObject()"}]},{"type":"text","text":" 方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void writeObject(java.io.ObjectOutputStream s)\n throws java.io.IOException {\n // Write out any hidden serialization magic\n s.defaultWriteObject();\n\n // Write out size\n s.writeInt(size);\n\n // Write out all elements in the proper order.\n for (LinkedList.Node x = first; x != null; x = x.next)\n s.writeObject(x.item);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發現沒?LinkedList 在序列化的時候只保留了元素的內容 item,並沒有保留元素的前後引用。這樣就節省了不少內存空間,對吧?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那有些小夥伴可能就疑惑了,只保留元素內容,不保留前後引用,那反序列化的時候怎麼辦?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void readObject(java.io.ObjectInputStream s)\n throws java.io.IOException, ClassNotFoundException {\n // Read in any hidden serialization magic\n s.defaultReadObject();\n\n // Read in size\n int size = s.readInt();\n\n // Read in all elements in the proper order.\n for (int i = 0; i < size; i++)\n linkLast((E)s.readObject());\n}\n\nvoid linkLast(E e) {\n final LinkedList.Node l = last;\n final LinkedList.Node newNode = new LinkedList.Node<>(l, e, null);\n last = newNode;\n if (l == null)\n first = newNode;\n else\n l.next = newNode;\n size++;\n modCount++;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意 for 循環中的 "},{"type":"codeinline","content":[{"type":"text","text":"linkLast()"}]},{"type":"text","text":" 方法,它可以把鏈表重新鏈接起來,這樣就恢復了鏈表序列化之前的順序。很妙,對吧?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"和 ArrayList 相比,LinkedList 沒有實現 RandomAccess 接口,這是因爲 LinkedList 存儲數據的內存地址是不連續的,所以不支持隨機訪問。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"03、ArrayList 和 LinkedList 新增元素時究竟誰快?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面我們已經從多個維度瞭解了 ArrayList 和 LinkedList 的實現原理和各自的特點。那接下來,我們就來聊聊 ArrayList 和 LinkedList 在新增元素時究竟誰快?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"1)ArrayList"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 新增元素有兩種情況,一種是直接將元素添加到數組末尾,一種是將元素插入到指定位置。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"添加到數組末尾的源碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public boolean add(E e) {\n modCount++;\n add(e, elementData, size);\n return true;\n}\n\nprivate void add(E e, Object[] elementData, int s) {\n if (s == elementData.length)\n elementData = grow();\n elementData[s] = e;\n size = s + 1;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很簡單,先判斷是否需要擴容,然後直接通過索引將元素添加到末尾。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"插入到指定位置的源碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void add(int index, E element) {\n rangeCheckForAdd(index);\n modCount++;\n final int s;\n Object[] elementData;\n if ((s = size) == (elementData = this.elementData).length)\n elementData = grow();\n System.arraycopy(elementData, index,\n elementData, index + 1,\n s - index);\n elementData[index] = element;\n size = s + 1;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先檢查插入的位置是否在合理的範圍之內,然後判斷是否需要擴容,再把該位置以後的元素複製到新添加元素的位置之後,最後通過索引將元素添加到指定的位置。這種情況是非常傷的,性能會比較差。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"2)LinkedList"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList 新增元素也有兩種情況,一種是直接將元素添加到隊尾,一種是將元素插入到指定位置。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"添加到隊尾的源碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public boolean add(E e) {\n linkLast(e);\n return true;\n}\nvoid linkLast(E e) {\n final LinkedList.Node l = last;\n final LinkedList.Node newNode = new LinkedList.Node<>(l, e, null);\n last = newNode;\n if (l == null)\n first = newNode;\n else\n l.next = newNode;\n size++;\n modCount++;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先將隊尾的節點 last 存放到臨時變量 l 中(不是說不建議使用 I 作爲變量名嗎?Java 的作者們明知故犯啊),然後生成新的 Node 節點,並賦給 last,如果 l 爲 null,說明是第一次添加,所以 first 爲新的節點;否則將新的節點賦給之前 last 的 next。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"插入到指定位置的源碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void add(int index, E element) {\n checkPositionIndex(index);\n\n if (index == size)\n linkLast(element);\n else\n linkBefore(element, node(index));\n}\nLinkedList.Node node(int index) {\n // assert isElementIndex(index);\n\n if (index < (size >> 1)) {\n LinkedList.Node x = first;\n for (int i = 0; i < index; i++)\n x = x.next;\n return x;\n } else {\n LinkedList.Node x = last;\n for (int i = size - 1; i > index; i--)\n x = x.prev;\n return x;\n }\n}\nvoid linkBefore(E e, LinkedList.Node succ) {\n // assert succ != null;\n final LinkedList.Node pred = succ.prev;\n final LinkedList.Node newNode = new LinkedList.Node<>(pred, e, succ);\n succ.prev = newNode;\n if (pred == null)\n first = newNode;\n else\n pred.next = newNode;\n size++;\n modCount++;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先檢查插入的位置是否在合理的範圍之內,然後判斷插入的位置是否是隊尾,如果是,添加到隊尾;否則執行 "},{"type":"codeinline","content":[{"type":"text","text":"linkBefore()"}]},{"type":"text","text":" 方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在執行 "},{"type":"codeinline","content":[{"type":"text","text":"linkBefore()"}]},{"type":"text","text":" 方法之前,會調用 "},{"type":"codeinline","content":[{"type":"text","text":"node()"}]},{"type":"text","text":" 方法查找指定位置上的元素,這一步是需要遍歷 LinkedList 的。如果插入的位置靠前前半段,就從隊頭開始往後找;否則從隊尾往前找。也就是說,如果插入的位置越靠近 LinkedList 的中間位置,遍歷所花費的時間就越多。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"找到指定位置上的元素(succ)之後,就開始執行 "},{"type":"codeinline","content":[{"type":"text","text":"linkBefore()"}]},{"type":"text","text":" 方法了,先將 succ 的前一個節點(prev)存放到臨時變量 pred 中,然後生成新的 Node 節點(newNode),並將 succ 的前一個節點變更爲 newNode,如果 pred 爲 null,說明插入的是隊頭,所以 first 爲新節點;否則將 pred 的後一個節點變更爲 newNode。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/27/27463bd2c268382f3bd008b945bc5936.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過源碼分析以後,小夥伴們是不是在想:“好像 ArrayList 在新增元素的時候效率並不一定比 LinkedList 低啊!”"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當兩者的起始長度是一樣的情況下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果是從集合的頭部新增元素,ArrayList 花費的時間應該比 LinkedList 多,因爲需要對頭部以後的元素進行復制。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ArrayListTest {\n public static void addFromHeaderTest(int num) {\n ArrayList list = new ArrayList(num);\n int i = 0;\n\n long timeStart = System.currentTimeMillis();\n\n while (i < num) {\n list.add(0, i + \"沉默王二\");\n i++;\n }\n long timeEnd = System.currentTimeMillis();\n\n System.out.println(\"ArrayList從集合頭部位置新增元素花費的時間\" + (timeEnd - timeStart));\n }\n}\n\n/**\n * @author 微信搜「沉默王二」,回覆關鍵字 PDF\n */\npublic class LinkedListTest {\n public static void addFromHeaderTest(int num) {\n LinkedList list = new LinkedList();\n int i = 0;\n long timeStart = System.currentTimeMillis();\n while (i < num) {\n list.addFirst(i + \"沉默王二\");\n i++;\n }\n long timeEnd = System.currentTimeMillis();\n\n System.out.println(\"LinkedList從集合頭部位置新增元素花費的時間\" + (timeEnd - timeStart));\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"num 爲 10000,代碼實測後的時間如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"ArrayList從集合頭部位置新增元素花費的時間595\nLinkedList從集合頭部位置新增元素花費的時間15"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 花費的時間比 LinkedList 要多很多。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果是從集合的中間位置新增元素,ArrayList 花費的時間搞不好要比 LinkedList 少,因爲 LinkedList 需要遍歷。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ArrayListTest {\n public static void addFromMidTest(int num) {\n ArrayList list = new ArrayList(num);\n int i = 0;\n\n long timeStart = System.currentTimeMillis();\n while (i < num) {\n int temp = list.size();\n list.add(temp / 2 + \"沉默王二\");\n i++;\n }\n long timeEnd = System.currentTimeMillis();\n\n System.out.println(\"ArrayList從集合中間位置新增元素花費的時間\" + (timeEnd - timeStart));\n }\n}\n\npublic class LinkedListTest {\n public static void addFromMidTest(int num) {\n LinkedList list = new LinkedList();\n int i = 0;\n long timeStart = System.currentTimeMillis();\n while (i < num) {\n int temp = list.size();\n list.add(temp / 2, i + \"沉默王二\");\n i++;\n }\n long timeEnd = System.currentTimeMillis();\n\n System.out.println(\"LinkedList從集合中間位置新增元素花費的時間\" + (timeEnd - timeStart));\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"num 爲 10000,代碼實測後的時間如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"ArrayList從集合中間位置新增元素花費的時間1\nLinkedList從集合中間位置新增元素花費的時間101"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 花費的時間比 LinkedList 要少很多很多。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果是從集合的尾部新增元素,ArrayList 花費的時間應該比 LinkedList 少,因爲數組是一段連續的內存空間,也不需要複製數組;而鏈表需要創建新的對象,前後引用也要重新排列。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ArrayListTest {\n public static void addFromTailTest(int num) {\n ArrayList list = new ArrayList(num);\n int i = 0;\n\n long timeStart = System.currentTimeMillis();\n\n while (i < num) {\n list.add(i + \"沉默王二\");\n i++;\n }\n\n long timeEnd = System.currentTimeMillis();\n\n System.out.println(\"ArrayList從集合尾部位置新增元素花費的時間\" + (timeEnd - timeStart));\n }\n}\n\npublic class LinkedListTest {\n public static void addFromTailTest(int num) {\n LinkedList list = new LinkedList();\n int i = 0;\n long timeStart = System.currentTimeMillis();\n while (i < num) {\n list.add(i + \"沉默王二\");\n i++;\n }\n long timeEnd = System.currentTimeMillis();\n\n System.out.println(\"LinkedList從集合尾部位置新增元素花費的時間\" + (timeEnd - timeStart));\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"num 爲 10000,代碼實測後的時間如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"ArrayList從集合尾部位置新增元素花費的時間69\nLinkedList從集合尾部位置新增元素花費的時間193"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 花費的時間比 LinkedList 要少一些。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣的結論和預期的是不是不太相符?ArrayList 在添加元素的時候如果不涉及到擴容,性能在兩種情況下(中間位置新增元素、尾部新增元素)比 LinkedList 好很多,只有頭部新增元素的時候比 LinkedList 差,因爲數組複製的原因。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然了,如果涉及到數組擴容的話,ArrayList 的性能就沒那麼可觀了,因爲擴容的時候也要複製數組。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"04、ArrayList 和 LinkedList 刪除元素時究竟誰快?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"1)ArrayList"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ArrayList 刪除元素的時候,有兩種方式,一種是直接刪除元素("},{"type":"codeinline","content":[{"type":"text","text":"remove(Object)"}]},{"type":"text","text":"),需要直先遍歷數組,找到元素對應的索引;一種是按照索引刪除元素("},{"type":"codeinline","content":[{"type":"text","text":"remove(int)"}]},{"type":"text","text":")。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public boolean remove(Object o) {\n final Object[] es = elementData;\n final int size = this.size;\n int i = 0;\n found: {\n if (o == null) {\n for (; i < size; i++)\n if (es[i] == null)\n break found;\n } else {\n for (; i < size; i++)\n if (o.equals(es[i]))\n break found;\n }\n return false;\n }\n fastRemove(es, i);\n return true;\n}\npublic E remove(int index) {\n Objects.checkIndex(index, size);\n final Object[] es = elementData;\n\n @SuppressWarnings(\"unchecked\") E oldValue = (E) es[index];\n fastRemove(es, index);\n\n return oldValue;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但從本質上講,都是一樣的,因爲它們最後調用的都是 "},{"type":"codeinline","content":[{"type":"text","text":"fastRemove(Object, int)"}]},{"type":"text","text":" 方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void fastRemove(Object[] es, int i) {\n modCount++;\n final int newSize;\n if ((newSize = size - 1) > i)\n System.arraycopy(es, i + 1, es, i, newSize - i);\n es[size = newSize] = null;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從源碼可以看得出,只要刪除的不是最後一個元素,都需要數組重組。刪除的元素位置越靠前,代價就越大。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"2)LinkedList"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LinkedList 刪除元素的時候,有四種常用的方式:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"remove(int)"}]},{"type":"text","text":",刪除指定位置上的元素"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public E remove(int index) {\n checkElementIndex(index);\n return unlink(node(index));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先檢查索引,再調用 "},{"type":"codeinline","content":[{"type":"text","text":"node(int)"}]},{"type":"text","text":" 方法( 前後半段遍歷,和新增元素操作一樣)找到節點 Node,然後調用 "},{"type":"codeinline","content":[{"type":"text","text":"unlink(Node)"}]},{"type":"text","text":" 解除節點的前後引用,同時更新前節點的後引用和後節點的前引用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" E unlink(Node x) {\n // assert x != null;\n final E element = x.item;\n final Node next = x.next;\n final Node prev = x.prev;\n\n if (prev == null) {\n first = next;\n } else {\n prev.next = next;\n x.prev = null;\n }\n\n if (next == null) {\n last = prev;\n } else {\n next.prev = prev;\n x.next = null;\n }\n\n x.item = null;\n size--;\n modCount++;\n return element;\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"remove(Object)"}]},{"type":"text","text":",直接刪除元素"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public boolean remove(Object o) {\n if (o == null) {\n for (LinkedList.Node x = first; x != null; x = x.next) {\n if (x.item == null) {\n unlink(x);\n return true;\n }\n }\n } else {\n for (LinkedList.Node x = first; x != null; x = x.next) {\n if (o.equals(x.item)) {\n unlink(x);\n return true;\n }\n }\n }\n return false;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也是先前後半段遍歷,找到要刪除的元素後調用 "},{"type":"codeinline","content":[{"type":"text","text":"unlink(Node)"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"removeFirst()"}]},{"type":"text","text":",刪除第一個節點"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public E removeFirst() {\n final LinkedList.Node f = first;\n if (f == null)\n throw new NoSuchElementException();\n return unlinkFirst(f);\n}\nprivate E unlinkFirst(LinkedList.Node f) {\n // assert f == first && f != null;\n final E element = f.item;\n final LinkedList.Node next = f.next;\n f.item = null;\n f.next = null; // help GC\n first = next;\n if (next == null)\n last = null;\n else\n next.prev = null;\n size--;\n modCount++;\n return element;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"刪除第一個節點就不需要遍歷了,只需要把第二個節點更新爲第一個節點即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"removeLast()"}]},{"type":"text","text":",刪除最後一個節點"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"刪除最後一個節點和刪除第一個節點類似,只需要把倒數第二個節點更新爲最後一個節點即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看得出,LinkedList 在刪除比較靠前和比較靠後的元素時,非常高效,但如果刪除的是中間位置的元素,效率就比較低了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏就不再做代碼測試了,感興趣的小夥伴可以自己試試,結果和新增元素保持一致:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從集合頭部刪除元素時,ArrayList 花費的時間比 LinkedList 多很多;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從集合中間位置刪除元素時,ArrayList 花費的時間比 LinkedList 少很多;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從集合尾部刪除元素時,ArrayList 花費的時間比 LinkedList 少一點。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我本地的統計結果如下所示,小夥伴們可以作爲參考:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"ArrayList從集合頭部位置刪除元素花費的時間380\nLinkedList從集合頭部位置刪除元素花費的時間4\nArrayList從集合中間位置刪除元素花費的時間381\nLinkedList從集合中間位置刪除元素花費的時間5922\nArrayList從集合尾部位置刪除元素花費的時間8\nLinkedList從集合尾部位置刪除元素花費的時間12"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"05、ArrayList 和 LinkedList 遍歷元素時究竟誰快?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"1)ArrayList"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"遍歷 ArrayList 找到某個元素的話,通常有兩種形式:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"get(int)"}]},{"type":"text","text":",根據索引找元素"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public E get(int index) {\n Objects.checkIndex(index, size);\n return elementData(index);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於 ArrayList 是由數組實現的,所以根據索引找元素非常的快,一步到位。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"indexOf(Object)"}]},{"type":"text","text":",根據元素找索引"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public int indexOf(Object o) {\n return indexOfRange(o, 0, size);\n}\n\nint indexOfRange(Object o, int start, int end) {\n Object[] es = elementData;\n if (o == null) {\n for (int i = start; i < end; i++) {\n if (es[i] == null) {\n return i;\n }\n }\n } else {\n for (int i = start; i < end; i++) {\n if (o.equals(es[i])) {\n return i;\n }\n }\n }\n return -1;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據元素找索引的話,就需要遍歷整個數組了,從頭到尾依次找。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"2)LinkedList"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"遍歷 LinkedList 找到某個元素的話,通常也有兩種形式:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"get(int)"}]},{"type":"text","text":",找指定位置上的元素"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public E get(int index) {\n checkElementIndex(index);\n return node(index).item;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然需要調用 "},{"type":"codeinline","content":[{"type":"text","text":"node(int)"}]},{"type":"text","text":" 方法,就意味着需要前後半段遍歷了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"indexOf(Object)"}]},{"type":"text","text":",找元素所在的位置"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public int indexOf(Object o) {\n int index = 0;\n if (o == null) {\n for (LinkedList.Node x = first; x != null; x = x.next) {\n if (x.item == null)\n return index;\n index++;\n }\n } else {\n for (LinkedList.Node x = first; x != null; x = x.next) {\n if (o.equals(x.item))\n return index;\n index++;\n }\n }\n return -1;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要遍歷整個鏈表,和 ArrayList 的 "},{"type":"codeinline","content":[{"type":"text","text":"indexOf()"}]},{"type":"text","text":" 類似。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那在我們對集合遍歷的時候,通常有兩種做法,一種是使用 for 循環,一種是使用迭代器(Iterator)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果使用的是 for 循環,可想而知 LinkedList 在 get 的時候性能會非常差,因爲每一次外層的 for 循環,都要執行一次 "},{"type":"codeinline","content":[{"type":"text","text":"node(int)"}]},{"type":"text","text":" 方法進行前後半段的遍歷。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"LinkedList.Node node(int index) {\n // assert isElementIndex(index);\n\n if (index < (size >> 1)) {\n LinkedList.Node x = first;\n for (int i = 0; i < index; i++)\n x = x.next;\n return x;\n } else {\n LinkedList.Node x = last;\n for (int i = size - 1; i > index; i--)\n x = x.prev;\n return x;\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那如果使用的是迭代器呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"LinkedList list = new LinkedList();\nfor (Iterator it = list.iterator(); it.hasNext();) {\n it.next();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"迭代器只會調用一次 "},{"type":"codeinline","content":[{"type":"text","text":"node(int)"}]},{"type":"text","text":" 方法,在執行 "},{"type":"codeinline","content":[{"type":"text","text":"list.iterator()"}]},{"type":"text","text":" 的時候:先調用 AbstractSequentialList 類的 "},{"type":"codeinline","content":[{"type":"text","text":"iterator()"}]},{"type":"text","text":" 方法,再調用 AbstractList 類的 "},{"type":"codeinline","content":[{"type":"text","text":"listIterator()"}]},{"type":"text","text":" 方法,再調用 LinkedList 類的 "},{"type":"codeinline","content":[{"type":"text","text":"listIterator(int)"}]},{"type":"text","text":" 方法,如下圖所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a2/a20ecc2f1645269776f241b5f7f36436.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後返回的是 LinkedList 類的內部私有類 ListItr 對象:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public ListIterator listIterator(int index) {\n checkPositionIndex(index);\n return new LinkedList.ListItr(index);\n}\n\nprivate class ListItr implements ListIterator {\n private LinkedList.Node lastReturned;\n private LinkedList.Node next;\n private int nextIndex;\n private int expectedModCount = modCount;\n\n ListItr(int index) {\n // assert isPositionIndex(index);\n next = (index == size) ? null : node(index);\n nextIndex = index;\n }\n\n public boolean hasNext() {\n return nextIndex < size;\n }\n\n public E next() {\n checkForComodification();\n if (!hasNext())\n throw new NoSuchElementException();\n\n lastReturned = next;\n next = next.next;\n nextIndex++;\n return lastReturned.item;\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"執行 ListItr 的構造方法時調用了一次 "},{"type":"codeinline","content":[{"type":"text","text":"node(int)"}]},{"type":"text","text":" 方法,返回第一個節點。在此之後,迭代器就執行 "},{"type":"codeinline","content":[{"type":"text","text":"hasNext()"}]},{"type":"text","text":" 判斷有沒有下一個,執行 "},{"type":"codeinline","content":[{"type":"text","text":"next()"}]},{"type":"text","text":" 方法下一個節點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由此,可以得出這樣的結論:"},{"type":"text","marks":[{"type":"strong"}],"text":"遍歷 LinkedList 的時候,千萬不要使用 for 循環,要使用迭代器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也就是說,for 循環遍歷的時候,ArrayList 花費的時間遠小於 LinkedList;迭代器遍歷的時候,兩者性能差不多。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"06、總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"花了兩天時間,終於肝完了!相信看完這篇文章後,再有面試官問你 ArrayList 和 LinkedList 有什麼區別的話,你一定會胸有成竹地和他扯上半小時。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"另外,我把自己看過的學習視頻按照順序分了類,共 500G,目錄如下,還有 2020 年最新面試題,現在免費送給大家"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/66/66a09ec280921c8df0f3608edce7eac5.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"鏈接:"},{"type":"link","attrs":{"href":"https://pan.baidu.com/s/1j2uB7-TF3t5BAzVXBgV7dA","title":""},"content":[{"type":"text","text":"https://pan.baidu.com/s/1j2uB7-TF3t5BAzVXBgV7dA"}]},{"type":"text","text":" 密碼:cg1q"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我是沉默王二,一枚沉默但有趣的程序員,感謝各位同學的:"},{"type":"text","marks":[{"type":"strong"}],"text":"點贊、收藏"},{"type":"text","text":"和評論,我們下篇見!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章