Java集合 - LinkedList底層源碼解析

以下是基于 JDK 8LinkedList 深度源碼解析,涵蓋其數據結構、核心方法實現、性能特點及使用場景。我們從 類結構、Node節點、插入/刪除/訪問操作、線程安全、性能對比 等角度進行詳細分析

一、類結構與繼承關系

1. 類定義

public class LinkedList<E> extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
  • AbstractSequentialList:提供按順序訪問元素的默認實現(如 get(int index)set(int index, E element))。
  • List:支持按索引操作(如 add(int index, E element)、get(int index))。
  • Deque:雙端隊列接口,支持頭尾插入/刪除(如 addFirst()removeLast())。
  • CloneableSerializable:支持克隆和序列化。

.

二、核心數據結構:Node節點

1. Node類定義

private static class Node<E> {E item;          // 當前節點存儲的元素Node<E> next;    // 指向下一個節點Node<E> prev;    // 指向前一個節點Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}
}
  • 雙向鏈表特性:每個節點通過 prevnext 指針連接,支持雙向遍歷。
  • 內存分配:節點分散存儲,不依賴連續內存(與 ArrayList 的數組不同)。

.

三、構造方法詳解

1. 無參構造函數

public LinkedList() {}
  • 初始化時 firstlast 均為 nullsize 為 0,表示鏈表為空。

2. 帶初始集合的構造函數

public LinkedList(Collection<? extends E> c) {this();addAll(c);
}
  • 調用無參構造函數后,通過 addAll() 方法將集合元素逐個添加到鏈表尾部。

.

四、插入操作詳解

1. 尾部插入:add(E e)

public boolean add(E e) {linkLast(e);return true;
}void linkLast(E e) {final Node<E> l = last;final Node<E> newNode = new Node<>(l, e, null);last = newNode;if (l == null)first = newNode;elsel.next = newNode;size++;modCount++;
}
  • 關鍵步驟
    1. 獲取當前尾節點 l
    2. 創建新節點 newNode,其 prev 指向 lnextnull
    3. 更新 lastnewNode
    4. 如果鏈表為空(l == null),則 firstlast 均指向 newNode
    5. 否則,將原尾節點的 next 指向 newNode
    6. 更新 sizemodCount(結構修改計數器,用于迭代器的 fail-fast 機制)。

2. 頭部插入:addFirst(E e)

private void linkFirst(E e) {final Node<E> f = first;final Node<E> newNode = new Node<>(null, e, f);first = newNode;if (f == null)last = newNode;elsef.prev = newNode;size++;modCount++;
}
  • 關鍵步驟
    1. 獲取當前頭節點 f
    2. 創建新節點 newNode,其 next 指向 fprevnull
    3. 更新 firstnewNode
    4. 如果鏈表為空(f == null),則 firstlast 均指向 newNode
    5. 否則,將原頭節點的 prev 指向 newNode

3. 中間插入:add(int index, E element)

public void add(int index, E element) {checkPositionIndex(index);if (index == size)linkLast(element);elselinkBefore(element, node(index));
}
  • 關鍵步驟
    1. 檢查索引合法性(0 ≤ index ≤ size)。
    2. 如果 index == size,調用 linkLast() 插入尾部。
    3. 否則,調用 node(index) 找到目標節點,再調用 linkBefore() 插入到該節點前。
linkBefore(E e, Node succ)
void linkBefore(E e, Node<E> succ) {final Node<E> pred = succ.prev;final Node<E> newNode = new Node<>(pred, e, succ);succ.prev = newNode;if (pred == null)first = newNode;elsepred.next = newNode;size++;modCount++;
}
  • 關鍵步驟
    1. 獲取目標節點 succ 的前驅節點 pred
    2. 創建新節點 newNode,其 prev 指向 prednext 指向 succ
    3. 更新 succ.prevnewNode
    4. 如果 pred == null(即插入到頭部),更新 first
    5. 否則,將 pred.next 指向 newNode

.

五、刪除操作詳解

1. 刪除指定元素:remove(Object o)

public boolean remove(Object o) {if (o == null) {for (Node<E> x = first; x != null; x = x.next) {if (x.item == null) {unlink(x);return true;}}} else {for (Node<E> x = first; x != null; x = x.next) {if (o.equals(x.item)) {unlink(x);return true;}}}return false;
}
  • 關鍵步驟
    1. 遍歷鏈表查找匹配的節點。
    2. 找到后調用 unlink(x) 刪除節點。
unlink(Node x)
E unlink(Node<E> x) {final E element = x.item;final Node<E> next = x.next;final Node<E> prev = x.prev;if (prev == null) {first = next;} else {prev.next = next;x.prev = null;}if (next == null) {last = prev;} else {next.prev = prev;x.next = null;}x.item = null;size--;modCount++;return element;
}
  • 關鍵步驟
    1. 獲取被刪除節點 x 的前后節點 prevnext
    2. 如果 prev == null,說明 x 是頭節點,更新 firstnext
    3. 否則,將 prev.next 指向 next,并斷開 x.prev
    4. 如果 next == null,說明 x 是尾節點,更新 lastprev
    5. 否則,將 next.prev 指向 prev,并斷開 x.next
    6. x.item 置為 null,幫助 GC 回收。
    7. 更新 sizemodCount

2. 刪除頭節點:removeFirst()

private E unlinkFirst(Node<E> f) {final E element = f.item;final Node<E> next = f.next;f.item = null;f.next = null;first = next;if (next == null)last = null;elsenext.prev = null;size--;modCount++;return element;
}
  • 關鍵步驟
    1. 獲取頭節點 fitemnext
    2. 斷開 f 的引用(f.item = null、f.next = null)
    3. 更新 firstnext
    4. 如果 next == null,說明鏈表為空,更新 lastnull
    5. 否則,將 next.prev 置為 null

.

六、訪問操作詳解

1. 按索引訪問:get(int index)

public E get(int index) {checkElementIndex(index);return node(index).item;
}Node<E> node(int 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;}
}
  • 關鍵步驟

    1. 根據索引位置選擇從頭節點或尾節點開始遍歷。
    2. 如果 index < size/2,從頭節點向后遍歷;否則,從尾節點向前遍歷。
    3. 返回對應節點的 item
  • 時間復雜度O(n),因為需要遍歷鏈表。

.

七、線程安全與 Fail-Fast 機制

1. 線程不安全

LinkedList 不是線程安全的,多線程環境下可能導致數據不一致或 ConcurrentModificationException

2. Fail-Fast 機制

  • 通過 modCount 記錄結構修改次數(如 addremove)。
  • 迭代器遍歷時會檢查 modCount 是否與創建迭代器時的值一致。如果不一致,拋出 ConcurrentModificationException

示例代碼

public Iterator<E> iterator() {return new Itr();
}private class Itr implements Iterator<E> {int expectedModCount = modCount;public boolean hasNext() {checkForComodification();return cursor != size;}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}
}

.

八、性能對比與使用建議

在這里插入圖片描述

使用建議

  • 頻繁插入/刪除:優先使用 LinkedList(尤其是頭部或尾部操作)。
  • 頻繁隨機訪問:優先使用 ArrayList
  • 線程安全:使用 Collections.synchronizedList(new LinkedList<>())CopyOnWriteArrayList
  • 雙端隊列LinkedList 實現了 Deque 接口,可作為雙端隊列使用(如棧、隊列)。

.

九、擴展學習建議

  1. 源碼調試:使用 IDE(如 IntelliJ IDEA)調試 LinkedListaddremoveget 方法,觀察 firstlastsize 的變化。
  2. 版本差異:對比 JDK 8 和 JDK 21 的 LinkedList 源碼,觀察是否引入新的優化(如 Spliterator 支持)。
  3. 進階主題
    • LinkedListVector 的區別(線程安全 vs 性能)。
    • Deque 接口的實現細節(如 ArrayDequeLinkedList 的對比)。
    • LinkedList 在 JVM 內存模型中的表現(鏈表的離散性 vs 數組的連續性)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/87077.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/87077.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/87077.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Pytorch 卷積神經網絡參數說明一

系列文章目錄 文章目錄 系列文章目錄前言一、卷積層的定義1.常見的卷積操作2. 感受野3. 如何理解參數量和計算量4.如何減少計算量和參數量 二、神經網絡結構&#xff1a;有些層前面文章說過&#xff0c;不全講1. 池化層&#xff08;下采樣&#xff09;2. 上采樣3. 激活層、BN層…

C++ 中的 iostream 庫:cin/cout 基本用法

iostream 是 C 標準庫中用于輸入輸出操作的核心庫&#xff0c;它基于面向對象的設計&#xff0c;提供了比 C 語言的 stdio.h 更強大、更安全的 I/O 功能。下面詳細介紹 iostream 庫中最常用的輸入輸出工具&#xff1a;cin 和 cout。 一、 基本概念 iostream 庫&#xff1a;包…

SAP復制一個自定義移動類型

SAP復制移動類型 在SAP系統中&#xff0c;復制移動類型201可以通過事務碼OMJJ或SPRO路徑完成&#xff0c;用于創建自定義的移動類型以滿足特定業務需求。 示例操作步驟 進入OMJJ事務碼&#xff1a; 打開事務碼OMJJ&#xff0c;選擇“移動類型”選項。 復制移動類型&#xff…

Bambu Studio 中的“回抽“與“裝填回抽“的區別

回抽 裝填回抽: Bambu Studio 中的“回抽” (Retraction) 和“裝填回抽”(Prime/Retract) 是兩個不同的概念&#xff0c;它們都與材料擠出機的操作過程相關&#xff0c;但作用和觸發條件有所不同。 回抽(Retraction): 回抽的作用, 在打印機移動到另一個位置之前&#xff0c;將…

危化品安全監測數據分析挖掘范式:從被動響應到戰略引擎的升維之路

在危化品生產的復雜生態系統中,安全不僅僅是合規性要求,更是企業生存和發展的生命線。傳統危化品安全生產風險監測預警系統雖然提供了基礎保障,但其“事后響應”和“單點預警”的局限性日益凸顯。我們正處在一個由大數據、人工智能、數字孿生和物聯網技術驅動的范式變革前沿…

C++ RPC 遠程過程調用詳細解析

一、RPC 基本原理 RPC (Remote Procedure Call) 是一種允許程序調用另一臺計算機上子程序的協議,而不需要程序員顯式編碼這個遠程交互細節。其核心思想是使遠程調用看起來像本地調用一樣。 RPC 工作流程 客戶端調用:客戶端調用本地存根(stub)方法參數序列化:客戶端存根將參…

Python:操作 Excel 預設色

??親愛的技術愛好者們,熱烈歡迎來到 Kant2048 的博客!我是 Thomas Kant,很開心能在CSDN上與你們相遇~?? 本博客的精華專欄: 【自動化測試】 【測試經驗】 【人工智能】 【Python】 Python 操作 Excel 系列 讀取單元格數據按行寫入設置行高和列寬自動調整行高和列寬水平…

中科院1區|IF10+:加大醫學系團隊利用GPT-4+電子病歷分析,革新肝硬化并發癥隊列識別

中科院1區|IF10&#xff1a;加大醫學系團隊利用GPT-4電子病歷分析&#xff0c;革新肝硬化并發癥隊列識別 在當下的科研領域&#xff0c;人工智能尤其是大語言模型的迅猛發展&#xff0c;正為各個學科帶來前所未有的機遇與變革。在醫學范疇&#xff0c;從疾病的早期精準篩查&am…

Python學習小結

bg&#xff1a;記錄一下&#xff0c;怕忘了&#xff1b;先寫一點&#xff0c;后面再補充。 1、沒有方法重載 2、字段都是公共字段 3、都是類似C#中頂級語句的寫法 4、對類的定義直接&#xff1a; class Student: 創建對象不需要new關鍵字&#xff0c;直接stu Student() 5、方…

QCustomPlot 中實現拖動區域放大?與恢復

1、拖動區域放大? 在 QCustomPlot 中實現 ?拖動區域放大?&#xff08;即通過鼠標左鍵拖動繪制矩形框選區域進行放大&#xff09;的核心方法是設置 SelectionRectMode。具體操作步驟&#xff1a; 1?&#xff09;禁用拖動模式? 確保先關閉默認的圖表拖動功能&#xff08;否…

如何將文件從 iPhone 傳輸到閃存驅動器

您想將文件從 iPhone 或 iPad 傳輸到閃存盤進行備份嗎&#xff1f;這是一個很好的決定&#xff0c;但您需要先了解一些實用的方法。雖然 Apple 生態系統在很大程度上是封閉的&#xff0c;但您可以使用一些實用工具將文件從 iPhone 或 iPad 傳輸到閃存盤。下文提供了這些行之有效…

互聯網大廠Java求職面試:云原生架構與微服務設計中的復雜挑戰

互聯網大廠Java求職面試&#xff1a;云原生架構與微服務設計中的復雜挑戰 面試官開場白 面試官&#xff08;嚴肅模式開啟&#xff09;&#xff1a;鄭薪苦&#xff0c;歡迎來到我們的技術面試環節。我是本次面試的技術總監&#xff0c;接下來我們將圍繞云原生架構、微服務設計、…

leetcode-hot-100 (鏈表)

1. 相交鏈表 題目鏈接&#xff1a;相交鏈表 題目描述&#xff1a;給你兩個單鏈表的頭節點 headA 和 headB &#xff0c;請你找出并返回兩個單鏈表相交的起始節點。如果兩個鏈表不存在相交節點&#xff0c;返回 null 。 解答&#xff1a; 其實這道題目我一開始沒太看懂題目給…

Web前端基礎之HTML

一、瀏覽器 火狐瀏覽器、谷歌瀏覽器(推薦)、IE瀏覽器 推薦谷歌瀏覽器原因&#xff1a; 1、簡潔大方,打開速度快 2、開發者調試工具&#xff08;右鍵空白處->檢查&#xff0c;打開調試模式&#xff09; 二、開發工具 核心IDE工具 Visual Studio Code (VS Code)? 微軟開發…

11.TCP三次握手

TCP連接建立與傳輸 1&#xff0e;主機 A 與主機 B 使用 TCP 傳輸數據&#xff0c;A 是 TCP 客戶&#xff0c;B 是 TCP 服務器。假設有512B 的數據要傳輸給 B&#xff0c;B 僅給 A 發送確認&#xff1b;A 的發送窗口 swnd 的尺寸為 100B&#xff0c;而 TCP 數據報文段每次也攜帶…

Python 爬蟲入門 Day 3 - 實現爬蟲多頁抓取與翻頁邏輯

Python 第二階段 - 爬蟲入門 &#x1f3af; 今日目標 掌握網頁分頁的原理和定位“下一頁”的鏈接能編寫循環邏輯自動翻頁抓取內容將多頁抓取整合到爬蟲系統中 &#x1f4d8; 學習內容詳解 &#x1f501; 網頁分頁邏輯介紹 以 quotes.toscrape.com 為例&#xff1a; 首頁鏈…

分布式定時任務系列12:XXL-job的任務觸發為什么是死循環?

傳送門 分布式定時任務系列1&#xff1a;XXL-job安裝 分布式定時任務系列2&#xff1a;XXL-job使用 分布式定時任務系列3&#xff1a;任務執行引擎設計 分布式定時任務系列4&#xff1a;任務執行引擎設計續 分布式定時任務系列5&#xff1a;XXL-job中blockingQueue的應用 …

位運算詳解之異或運算的奇妙操作

位運算詳解之異或運算的奇妙操作 一、異或運算的本質與核心性質1.1 異或運算的定義與邏輯規則1.2 異或運算的核心代數性質&#xff08;1&#xff09;自反性&#xff1a;a ^ a 0&#xff08;2&#xff09;恒等性&#xff1a;a ^ 0 a&#xff08;3&#xff09;交換律&#xff1…

Element Plus 去除下拉菜單周黑邊

問題&#xff1a; 如上圖所示&#xff0c;當鼠標移入&#xff08;hover&#xff09;和點擊時就會圍繞一圈黑色邊框&#xff0c;但通過本文的方案 100% 完美解決。 解決方案: :deep(:focus-visible) {outline: none; } 備用方案 :deep(.el-tooltip__trigger:focus-visible) …

React Native 項目實戰 —— 記賬本應用開發指南

React Native 項目實戰 —— 記賬本應用開發指南 項目概述&#xff1a;本文將指導您使用 React Native 開發一個簡單的記賬本應用&#xff0c;幫助用戶記錄收入和支出。核心內容&#xff1a;我們將分析功能模塊、設計接口、劃分組件結構、管理數據流、實現頁面跳轉&#xff0c…