Java 中 LinkedList 的底層源碼

????????在 Java 的集合框架中,LinkedList是一個獨特且常用的成員。它基于雙向鏈表實現,與數組結構的集合類如ArrayList有著顯著差異。深入探究LinkedList的底層源碼,有助于我們更好地理解其工作原理和性能特點,以便在實際開發中做出更合適的選擇。

????????這里如何具體在idea里查看底層源碼的教程在我ArrayList那篇文章有,基本大差不差,具體步驟我就不再演示了,我直接把所有底層源碼總結下來供大家參考。

咱們可以根據這個代碼邏輯自己推一下,捋清楚了思路就好理解多了。

一、基本結構與成員變量

LinkedList的底層核心是一個雙向鏈表,其內部通過節點來存儲數據。以下是LinkedList源碼中關鍵的成員變量定義:

// 頭節點
transient Node<E> first;
// 尾節點
transient Node<E> last;
// 元素數量
transient int size = 0;
// 底層雙向鏈表節點的內部類
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;}
}

firstlast分別指向雙向鏈表的頭節點和尾節點,通過它們可以方便地對鏈表進行遍歷和操作。size變量用于記錄鏈表中元素的個數。Node內部類則定義了鏈表節點的結構,每個節點不僅存儲了元素值item,還持有指向前驅節點prev和后繼節點next的引用,這使得雙向鏈表能夠靈活地在兩個方向上進行遍歷和元素的插入、刪除等操作。

二、構造函數剖析

無參構造函數

public LinkedList() {
}

無參構造函數非常簡潔,它只是創建了一個空的LinkedList對象。此時,firstlast都為nullsize為 0,鏈表中沒有任何元素。

帶集合參數的構造函數

public LinkedList(Collection<? extends E> c) {this();addAll(c);
}

該構造函數先調用無參構造函數創建一個空鏈表,然后通過addAll方法將傳入集合中的所有元素添加到新創建的LinkedList中。這為我們在初始化LinkedList時提供了一種便捷的方式,可以直接將其他集合中的元素導入進來。

三、元素添加操作

在鏈表尾部添加元素

add(E e)方法用于在鏈表尾部添加一個元素,它是LinkedList中最常用的添加元素的方式之一。

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++;
}

add(E e)方法內部調用了linkLast方法。linkLast方法首先記錄原鏈表的尾節點l,然后創建一個新的節點newNode,使其前驅指向原尾節點l,后繼為null。接著更新last指向新節點,如果原尾節點為空,說明鏈表之前是空的,此時first也指向新節點;否則將原尾節點的后繼指向新節點。最后,更新元素數量size和修改次數modCount。這個過程使得在鏈表尾部添加元素的操作非常高效,時間復雜度為 O (1)。

在指定位置插入元素

add(int index, E element)方法可以在鏈表的指定位置插入一個元素。

public void add(int index, E element) {checkPositionIndex(index);if (index == size)linkLast(element);elselinkBefore(element, node(index));
}

該方法首先調用checkPositionIndex方法檢查索引是否越界(要求0 <= index <= size)。如果索引等于size,說明要在鏈表尾部插入元素,直接調用linkLast方法即可;否則,調用linkBefore方法在指定節點前插入元素。這里的node(index)方法用于獲取指定索引位置的節點:

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;}
}

node(index)方法通過判斷索引與鏈表長度一半的大小關系,決定從鏈表頭部還是尾部開始遍歷查找指定索引位置的節點。如果索引小于鏈表長度的一半,從鏈表頭部開始遍歷;否則從鏈表尾部開始遍歷。這種優化策略可以減少遍歷的節點數量,提高查找效率。

四、元素刪除操作

移除指定位置的元素

remove(int index)方法用于移除鏈表中指定位置的元素。

public E remove(int index) {checkElementIndex(index);return unlink(node(index));
}

該方法先調用checkElementIndex方法檢查索引是否越界(要求0 <= index < size),然后通過node(index)方法獲取指定索引位置的節點,最后調用unlink方法移除該節點并返回其存儲的元素。

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;
}

unlink方法通過調整節點之間的引用關系,將指定節點從鏈表中移除。它先保存要移除節點的元素值、后繼節點和前驅節點,然后根據前驅和后繼節點的情況更新鏈表的頭節點first或尾節點last,以及相關節點的前驅和后繼引用。最后將被移除節點的元素值置為null,更新元素數量size和修改次數modCount,并返回被移除的元素。

移除指定元素的第一個匹配項

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;
}

該方法通過遍歷鏈表,分別處理要移除的元素為null和不為null的情況。找到匹配的元素后,調用unlink方法將其移除并返回true;如果遍歷完鏈表都沒有找到匹配元素,則返回false

五、元素查找操作

查找指定元素首次出現的索引

indexOf(Object o)方法用于返回指定元素在鏈表中首次出現的索引,如果不存在則返回 -1。

public int indexOf(Object o) {int index = 0;if (o == null) {for (Node<E> x = first; x != null; x = x.next) {if (x.item == null)return index;index++;}} else {for (Node<E> x = first; x != null; x = x.next) {if (o.equals(x.item))return index;index++;}}return -1;
}

該方法從鏈表頭部開始遍歷,分別處理查找元素為null和不為null的情況,找到匹配元素時返回其索引,遍歷完鏈表都未找到則返回 -1。

查找指定元素最后一次出現的索引

lastIndexOf(Object o)方法用于返回指定元素在鏈表中最后一次出現的索引,如果不存在則返回 -1。

public int lastIndexOf(Object o) {int index = size;if (o == null) {for (Node<E> x = last; x != null; x = x.prev) {index--;if (x.item == null)return index;}} else {for (Node<E> x = last; x != null; x = x.prev) {index--;if (o.equals(x.item))return index;}}return -1;
}

該方法從鏈表尾部開始遍歷,分別處理查找元素為null和不為null的情況,找到匹配元素時返回其索引,遍歷完鏈表都未找到則返回 -1。

通過對LinkedList底層源碼的剖析,我們清楚地了解了它的結構和各種操作的實現原理。LinkedList在插入和刪除元素時具有高效性,尤其是在鏈表中間位置進行操作時,不需要像ArrayList那樣進行大量元素的移動。然而,由于其基于鏈表結構,隨機訪問元素的時間復雜度較高,需要遍歷鏈表來查找指定位置的元素。因此,在實際開發中,我們應根據具體的需求和操作場景,合理選擇使用LinkedList或其他集合類,以達到最佳的性能和功能實現。

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

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

相關文章

Level2逐筆成交逐筆委托數據分享下載:20250127

Level2逐筆成交逐筆委托數據分享下載 采用Level2逐筆成交與逐筆委托的毫秒級數據&#xff0c;可以揭露眾多有用信息&#xff0c;如莊家策略、偽裝交易&#xff0c;讓所有交易行為透明化。這對于交易高手的策略分析極為有用&#xff0c;對人工智能領域的機器學習也極為合適&…

金蝶云星空k3cloud webapi報“java.lang.Class cannot be cast to java.lang.String”的錯誤

最近在對接金蝶云星空k3cloud webapi時&#xff0c;報一個莫名其妙的轉換異常&#xff0c;具體如下&#xff1a; 同步部門異常! ERP接口登錄異常&#xff1a;java.lang.Class cannot be cast to java.lang.String at com.jkwms.k3cloudSyn.service.basics.DeptK3CloudService.…

【Android】jni開發之導入opencv和libyuv來進行圖像處理

做視頻圖像處理時需要對其進行水印的添加&#xff0c;放在應用層調用工具性能方面不太滿意&#xff0c;于是當下采用opencvlibyuv方法進行處理。 對于Android的jni開發不是很懂&#xff0c;我的需求是導入opencv方便在cpp中調用&#xff0c;但目前找到的教程都是把opencv作為模…

【MySQL】centos 7 忘記數據庫密碼

vim /etc/my.cnf文件&#xff1b; 在[mysqld]后添加skip-grant-tables&#xff08;登錄時跳過權限檢查&#xff09; 重啟MySQL服務&#xff1a;sudo systemctl restart mysqld 登錄mysql&#xff0c;輸入mysql –uroot –p&#xff1b;直接回車&#xff08;Enter&#xff09; 輸…

國產編輯器EverEdit - 自定義標記使用詳解

1 自定義標記使用詳解 1.1 應用場景 當閱讀日志等文件&#xff0c;用于調試或者檢查問題時&#xff0c;往往日志中會有很多關鍵性的單詞&#xff0c;比如&#xff1a;ERROR, FATAL等&#xff0c;但由于文本模式對這些關鍵詞并沒有突出顯示&#xff0c;造成檢查問題時&#xff…

Golang 并發機制-6:掌握優雅的錯誤處理藝術

并發編程可能是提高軟件系統效率和響應能力的一種強有力的技術。它允許多個工作負載同時運行&#xff0c;充分利用現代多核cpu。然而&#xff0c;巨大的能力帶來巨大的責任&#xff0c;良好的錯誤管理是并發編程的主要任務之一。 并發代碼的復雜性 并發編程增加了順序程序所不…

數據庫并發策略

并發控制是數據庫管理中的一個重要方面&#xff0c;它確保多個事務能夠正確地訪問和修改數據&#xff0c;同時保持數據的一致性和完整性。樂觀鎖、悲觀鎖和時間戳是并發控制的三種主要方法。以下是對這三種方法的詳細解析&#xff0c;并結合實踐進行分析&#xff1a; 一、樂觀…

JVM 四虛擬機棧

虛擬機棧出現的背景 由于跨平臺性的設計&#xff0c;Java的指令都是根據棧來設計的。不同平臺CPU架構不同&#xff0c;所以不能設計為基于寄存器的。優點是跨平臺&#xff0c;指令集小&#xff0c;編譯器容易實現&#xff0c;缺點是性能下降&#xff0c;實現同樣的功能需要更多…

鼠標拖尾特效

文章目錄 鼠標拖尾特效一、引言二、實現原理1、監聽鼠標移動事件2、生成拖尾元素3、控制元素生命周期 三、代碼實現四、使用示例五、總結 鼠標拖尾特效 一、引言 鼠標拖尾特效是一種非常酷炫的前端交互效果&#xff0c;能夠為網頁增添獨特的視覺體驗。它通常通過JavaScript和C…

6-圖像金字塔與輪廓檢測

文章目錄 6.圖像金字塔與輪廓檢測(1)圖像金字塔定義(2)金字塔制作方法(3)輪廓檢測方法(4)輪廓特征與近似(5)模板匹配方法6.圖像金字塔與輪廓檢測 (1)圖像金字塔定義 高斯金字塔拉普拉斯金字塔 高斯金字塔:向下采樣方法(縮小) 高斯金字塔:向上采樣方法(放大)…

RNN/LSTM/GRU 學習筆記

文章目錄 RNN/LSTM/GRU一、RNN1、為何引入RNN&#xff1f;2、RNN的基本結構3、各種形式的RNN及其應用4、RNN的缺陷5、如何應對RNN的缺陷&#xff1f;6、BPTT和BP的區別 二、LSTM1、LSTM 簡介2、LSTM如何緩解梯度消失與梯度爆炸&#xff1f; 三、GRU四、參考文獻 RNN/LSTM/GRU …

異步程序設計方式

目錄 一、異步編程種類簡介 二、線程 三、回調 四、Future、 Promise 及其他 五、反應式擴展 六、協程 一、異步編程種類簡介 幾十年以來&#xff0c;作為開發人員&#xff0c;我們面臨著需要解決的問題——如何防止我們的應用程序被阻塞。 當我們正在開發桌面應用&#…

qt-Quick3D筆記之官方例程Runtimeloader Example運行筆記

qt-Quick3D筆記之官方例程Runtimeloader Example運行筆記 文章目錄 qt-Quick3D筆記之官方例程Runtimeloader Example運行筆記1.例程運行效果2.例程縮略圖3.項目文件列表4.main.qml5.main.cpp6.CMakeLists.txt 1.例程運行效果 運行該項目需要自己準備一個模型文件 2.例程縮略圖…

以太坊入門【詳解】

以太坊的組成部分 P2P網絡&#xff1a;以太坊在以太坊網絡上運行&#xff0c;該網絡可在TCP端口30303上尋址&#xff0c;并運行一個協議。交易&#xff1a;以太坊交易時網絡消息&#xff0c;其中包括發送者&#xff0c;接受者&#xff0c;值和數據的有效載荷以太坊虛擬機&…

實驗十四 EL和JSTL

實驗十四 EL和JSTL 一、實驗目的 1、掌握EL表達式的使用 2、掌握JSTL的使用 二、實驗過程 1、在數據庫Book中建立表Tbook&#xff0c;包含圖書ID&#xff0c;圖書名稱&#xff0c;圖書價格。實現在bookQuery.jsp頁面中模糊查詢圖書&#xff0c;如果圖書的價格在50元以上&#…

安裝和卸載RabbitMQ

我的飛書:https://rvg7rs2jk1g.feishu.cn/docx/SUWXdDb0UoCV86xP6b3c7qtMn6b 使用Ubuntu環境進行安裝 一、安裝Erlang 在安裝RabbitMQ之前,我們需要先安裝Erlang,RabbitMQ需要Erlang的語言支持 #安裝Erlang sudo apt-get install erlang 在安裝的過程中,會彈出一段信息,此…

音視頻多媒體編解碼器基礎-codec

如果要從事編解碼多媒體的工作&#xff0c;需要準備哪些更為基礎的內容&#xff0c;這里幫你總結完。 因為數據類型不同所以編解碼算法不同&#xff0c;分為圖像、視頻和音頻三大類&#xff1b;因為流程不同&#xff0c;可以分為編碼和解碼兩部分&#xff1b;因為編碼器實現不…

ML基礎-Jupyter notebook中的魔法命令

在 Jupyter Notebook 或 IPython 環境中&#xff0c;“魔法命令”&#xff08;Magic Commands&#xff09;是一些以百分號&#xff08;%&#xff09;或驚嘆號&#xff08;!)開頭的特殊命令&#xff0c;用于執行一些與代碼運行環境相關的操作&#xff0c;而不僅僅是執行普通的 P…

【Unity2D 2022:UI】創建滾動視圖

一、創建Scroll View游戲對象 在Canvas畫布下新建Scroll View游戲對象 二、為Content游戲對象添加Grid Layout Group&#xff08;網格布局組&#xff09;組件 選中Content游戲物體&#xff0c;點擊Add Competent添加組件&#xff0c;搜索Grid Layout Group組件 三、調整Grid La…

9-收納的知識

[ComponentOf(typeof(xxx))]組件描述&#xff0c;表示是哪個實體的組件 [EntitySystemOf(typeof(xxx))] 系統描述 [Event(SceneType.Demo)] 定義事件&#xff0c;在指定場景的指定事件發生后觸發 [ChildOf(typeof(ComputersComponent))] 標明是誰的子實體 [ResponseType(na…