【Java集合】LinkedList源碼深度分析

參考筆記:java LinkedList 源碼分析(通俗易懂)_linkedlist源碼分析-CSDN博客


目錄

1.前言

2.LinkedList簡介

3.LinkedList的底層實現

4.LinkedList 與 ArrayList 的對比

4.1 如何選擇

4.2 對比圖

5.LinkedList 源碼Debug

5.1 add(E e)

????????(1)跳入無參構造

????????(2) 跳入add方法

????????(3)跳入linkLast方法

????????(4)增加第一個元素成功?

????????(5)向鏈表中添加第二個元素

5.2 remove()

????????(1)準備工作

????????(2) 跳入remove()方法

????????(3)跳入removeFirst()方法

????????(4)跳入unlinkFirst()方法

????????(5) 第一個結點被刪除


1.前言

? ? ? ? ?本文是對單列集合 List 的實現類之一 LinkedList 的源碼解析。對于單列集合 List 的三個最常用的實現類—— ArrayList, Vector, LinkedList ,我對 ArrayList、Vector 的源碼解讀在另外兩篇文章,感興趣的話可以看看:

【Java集合】ArrayList源碼深度分析-CSDN博客https://blog.csdn.net/m0_55908255/article/details/146886585【Java集合】Vector源碼深度分析-CSDN博客https://blog.csdn.net/m0_55908255/article/details/146949740????????注意 :?本文對 LinkedList?源碼的解讀基于主流的?JDK 8.0?的版本

2.LinkedList簡介

?LinkedList 是一種常見的線性表,每一個結點中都存放了下一個結點的地址LinkedList 類位于 java.lang.LinkedList 下,其類定義和繼承關系圖如下:

??鏈表可分為單向鏈表和雙向鏈表

? ??? ? 單項鏈表的每個結點:包含兩個值——當前結點的值、下一個結點的地址值(以指向后一個結點)

? ? ? ??雙向鏈表的每個結點:包含三個值——前一個結點的地址值(以指向前一個結點)、當前結點的值、下一個結點的地址值(以指向后一個結點)

③??LinkedList 底層實現了雙向鏈表和雙端隊列的特點

?同 ArrayList、Vector 類似,可以向 LinkedList 集合中添加任意元素,包括 null,并且元素允許重復

?同 ArrayList 一樣,LinkedList?也沒有實現線程同步,因此?LinkedList 線程不安全

3.LinkedList的底層實現

LinkedList 的底層維護了一個雙向鏈表。?LinkedList 類中維護了 first、last 屬性,見名知意,它們分別指向雙向鏈表的首結點和尾結點。其在源碼中的定義如下:

②? 鏈表中的每個結點( Node 對象)中又維護了?prev,next,item 三個屬性,item 存放當前節點的值。通過 prev 指向前一個結點,通過 next 指向后一個結點,從而實現雙向鏈表。此處的 Node<E> 類型,實際上是 LinkedList?類中維護的一個靜態內部類,如下圖所示 :?

③?LinkedList 中元素的添加和刪除,在底層不是通過數組來完成的,而是通過鏈表來完成。因此 LinkedList 相對來說增刪的效率更高

補充:為什么鏈表的增刪效率比數組高?

相信學過數據結構的同學都知道,這里我簡單提一下。數組如果刪除中間的某一個元素可能需要挪動大量的數據,增加亦是如此。而鏈表只需要修改所刪除節點的前后節點的 next、prev 即可,比較方便

④??雙向鏈表的示意圖如下 : (注意箭頭是指向結點整體)

這里我用 Java 來模擬一個簡單的雙向鏈表,現在創建三個《誅仙》人物對象——陸雪琪、張小凡、小環,并且讓他們形成如下的雙向鏈表關系:?

代碼如下:

public class demo {public static void main(String[] args) {//演示 : 用java模擬一個簡單的雙向鏈表//創建誅仙人物對象Node luxueqi = new Node("陸雪琪");        //第一個結點Node zhangxiaofan = new Node("張小凡");   //第二個結點Node xiaohuan = new Node("小環");       //第三個結點//完成雙向鏈表//從左往右相連接luxueqi.next = zhangxiaofan;zhangxiaofan.next = xiaohuan;//從右往左相連接xiaohuan.prev= zhangxiaofan;zhangxiaofan.prev= luxueqi;//令first指向第一個結點,令last指向最后一個結點Node first = luxueqi;Node last = xiaohuan;//遍歷鏈表(頭 ——> 尾)System.out.println("從頭-->尾");while (true) {if (first == null) {break;}System.out.println(first);      //輸出當前對象的信息first = first.next;             //更改指向}System.out.println("=====================================");//遍歷鏈表(尾 ——> 頭)System.out.println("從尾-->頭");while (true) {if (last == null) {break;}System.out.println(last);       //輸出當前對象的信息last = last.prev;                //更改指向}}
}//自定義Node結點
class Node {public Object item;     //存放當前結點的數據public Node prev;       //指向前一個結點public Node next;       //指向后一個結點public Node(Object name) {this.item = name;}public String toString() {return "Node 's name = " + item;}
}

運行結果:

4.LinkedList 與 ArrayList 的對比

4.1 如何選擇

①?增刪操作多,優先選擇 LinkedList ;改查操作多,優先選擇 ArrayList

②?實際開發中,往往對集合的改查操作比較多,因此 ArrayList 一般用的較多

③?根據實際業務需求來選擇

④?ArrayListLinkedList 線程均不安全,建議單線程情況下使用

4.2 對比圖

5.LinkedList 源碼Debug

5.1 add(E e)

用以下代碼為演示,進行?Debug?操作:

import java.util.LinkedList;
public class demo {public static void main(String[] args) {LinkedList linkedList = new LinkedList();linkedList.add(11);linkedList.add(141);System.out.println(linkedList);}
}

????????(1)跳入無參構造

? ? ? ? 如下圖所示:

????????可以看到, LinkedList 類的無參構造其實什么也沒有做,這里只會利用隱藏的 super() 調用父類的無參構造器。因為它底層使用鏈表實現的,所以不需要創建數組

? ? ? ? 直接跳出無參構造,可以看到 LinkedList 對象已經初始化完畢, LinkedList 維護的 first、last 屬性經過了 默認初始化 ---> 顯式初始化 ---> 構造器初始化,最后仍為默認值 null,如下圖所示 :

????????此時 firstlast 均為 null。所以,鏈表此時的結構如下圖所示:

????????(2) 跳入add方法

? ? ? ? ?由于我們向鏈表中添加的元素為 int 類型,所以跳入 add 方法之前會進行自動裝箱 int ---> Integer,如下圖所示:

? ? ? ? 自動裝箱結束后,跳入 add 方法,如下圖所示:

????????形參列表的 "e" 表示我們當前要添加的元素,所以此時 e = 11add 方法中調用了 linkLast 方法,在 linkLast 方法里面完成了添加元素的操作

????????(3)跳入linkLast方法

? ? ? ? 跳入 linkLast 方法,如下圖所示:

????????一步一步來看

? ? ? ? 首先,Node 是"結點"的意思,就是 Node<E> 類型

? ? ? ? 其次,本文上面提到說——此時 first == null,last == null。所以,linkLast 方法內,第一步是定義了一個 Node 類型的常指針?\color{red}l,并令其指向為 last,所以此時?\color{red}l=last=null

????????接著,又定義了一個 Node 類型的常量 newNode 見名知意,"newNode" 就是我們要添加的新結點。那么,為 newNode 初始化的這個帶參構造 new Node<>(l,e,null) 是怎么執行的呢?這三個實參分別是干嘛的?

????????我們追入這個帶參構造看個究竟:

? ? ? ? 可以看到,構造器的三個形參就是 Node 對象的三個屬性。所以,對應此處的三個實參,\color{red}l?就是 prev,此時為 nulle 就是已經裝箱好的 11null 就是 next 的值

????????因此, newNode 引用此時指向的就是一個前后結點均為空,值為 11 的新結點

? ? ? ? 執行完帶參構造,就是?last = newNode,即又令 last 指向了添加的新結點,使得 last 始終指向鏈表中的最后一個結點,這是典型的鏈表尾插法


????????繼續向下執行,是一個 if-else 的復合條件語句。判斷條件 "l == null" 顯然滿足,令 first 也指向了該新結點;之后,又令 size 自增 1?(size表示當前鏈表中結點的個數),modCount 也自增 1(modCount表示鏈表的修改次數)

????????(4)增加第一個元素成功?

? ? ? ? linkLast 執行完畢后,此時的鏈表結構如下圖所示:

????????接下來,我們可以逐層跳出直到演示類中驗證一下上面的鏈表結構圖是否正確。此時鏈表的狀態,如下圖所示:

????????可以看到,firstlast 確實是指向了同一個結點,并且該結點中 prev = null,next = null,item = 11??

????????(5)向鏈表中添加第二個元素

????????向鏈表中添加第?2 個元素,前面幾個步驟都一樣,這里就不再贅述了,直接從 linkLast 方法開始說起,如下 :??

????????還是一步一步來看

????????首先,Node 類型的常指針?\color{red}l?指向了 last 所指向的結點(即我們前面添加的第一個結點,此時它也為鏈表中的最后一個結點)

????????其次,第二個新結點 newNode 進行初始化工作。注意,第一個實參?\color{red}l?代表的是新結點的prev,而?\color{red}l?此時指向的是第一個結點,因此,這一步實現了——令新節點 newNodeprev 指向了第一個結點

????????接著,執行完帶參構造,就是?last = newNode,即又令 last 指向了添加的新結點 newNode ,使得 last 始終指向鏈表中的最后一個結點,這是典型的鏈表尾插法

? ? ? ? 之后,就是 if-else 的判斷語句了,此時?\color{red}l?指向的是鏈表中的第一個結點,不為空,所以執行 else 中的語句,l.next = newNode,令第一個結點的 next 指向了新結點?

????????最后,就是更新 size 、modCount

? ? ? ? 綜上,第二次 linkLast 方法執行完畢后,鏈表結構如下圖所示:

🆗,以上就是 LinkedList add(E e) 方法源碼分析

5.2 remove()

LinkedList 類的 remove 有三個重載方法:

①?remove( ):刪除鏈表的第一個結點

②?remove(int index):刪除鏈表中第 index-1 個結點

?remove(Object o):刪除鏈表中與 o 值匹配的第一個結點

????????(1)準備工作

????????用以下代碼演示 remove() ,進行?Debug?操作:

import java.util.LinkedList;
public class demo {public static void main(String[] args) {LinkedList linkedList = new LinkedList();linkedList.add(11);linkedList.add(141);linkedList.add(5);System.out.println("添加三個元素后,當前鏈表 = " + linkedList);linkedList.remove();System.out.println("刪除第一個元素后,當前鏈表 = " + linkedList);}
}

????????運行結果:?

????????如代碼所示,先在鏈表中加入三個元素:11,141,5 。則在刪除元素之前,雙向鏈表的結構如下圖所示:

????????(2) 跳入remove()方法

?????????我們直接在 remove 方法的調用行設置斷點跳過去,并跳入 remove 方法,如下圖所示 :?

????????(3)跳入removeFirst()方法

????????removeFirst 方法的源碼如下:?

?????????首先,它令一個 Node 類型的常指針 f 指向了鏈表的第一個結點然后判斷首結點是否為空。由于我們一開始就在鏈表中添加了 3 個元素,所以此處 f 肯定不為空。因此, if 語句中的內容會跳過,執行 return unlinkFirst(f)

????????(4)跳入unlinkFirst()方法

? ? ? ? 跳入?unlinkFirst 方法,其源碼如下:

????????一步一步來看

????????首先第一條語句不用太關注。取出欲刪除結點的 item 值只是為了最后 return 返回,與刪除過程本身無關

????????其次,第二條語句 final Node<E> next = f.next,它令一個 Node 類型的常指針 next 指向了 "第一個結點的next屬性所指向的值",即指向了第二個結點,如下圖所示 :?

????????接著執行 f.item = null,f.next = null,置空了第一個結點的 itemnext ,并且令first 指向了第二個結點,如下圖所示 :??

????????繼續?由于 next 現在指向的是第二個結點,不為空,所以接下里要進入 else 語句中。else語句中,next.prev = null,即將第二個結點的 prev 置為 null ,如下圖所示 :??

? ? ? ? 最后就是 size - 1,即鏈表中的結點個數減 1 modCount + 1,鏈表修改次數加 1 return elment,返回刪除結點其對應的 item 值?

????????(5) 第一個結點被刪除

????????至此,鏈表中的第一個結點被刪除。回顧一下整個流程

????????① 將第一個結點的 item、next 置為 null

????????② first 指向了第二個結點

????????③ 將第二個結點的 prev 值置為 null切斷其與第一個結點的"聯系"

????????④ 經過①②③第一個結點被置于鏈表之外,之后會被 gc垃圾回收器 刪除

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

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

相關文章

openssl源碼分析之加密模式(modes)

openssl實現分組加密模式&#xff08;例如AES128-CBC的CBC部分&#xff09;的模塊名字叫做modes&#xff0c;源代碼位于 https://gitee.com/gh_mirrors/openssl/tree/master/crypto/modes 博主又打不開github了TT&#xff0c;只能找個gitee鏡像 頭文件是modes.h。 該模塊目前…

Java 搭建 MC 1.18.2 Forge 開發環境

推薦使用 IDEA 插件 Minecraft Development 進行創建項目 創建完成后即可進行 MOD 開發。 但是關于 1.18.2 的開發教程太少&#xff0c;因此自己研究了一套寫法&#xff0c;寫法并非是最優的但是是探索開發MOD中的一次筆記和記錄 GITHUB: https://github.com/zimoyin/zhenfa…

nginx如何實現負載均衡?

Nginx 是一款高性能的 Web 服務器和反向代理服務器&#xff0c;它可以通過配置實現負載均衡功能。以下是實現負載均衡的詳細步驟和方法&#xff1a; 1. 基本概念 負載均衡是將客戶端請求分發到多個后端服務器上&#xff0c;以提高系統的可用性和性能。Nginx 支持多種負載均衡策…

深度學習天崩開局

李沐大神的d2l包導入&#xff0c; 這玩意需要python311版本&#xff0c;我現在版本已經313了&#xff0c;作為一個天生要強的男人&#xff0c;我是堅決不向低版本低頭的。 然后我就研究啊&#xff0c;各種翻資料啊&#xff0c;然后deepseek加豆包都翻爛了&#xff0c; 最終所…

docker部署jenkins并成功自動化部署微服務

一、環境版本清單&#xff1a; docker 26.1.4JDK 17.0.28Mysql 8.0.27Redis 6.0.5nacos 2.5.1maven 3.8.8jenkins 2.492.2 二、服務架構&#xff1a;有gateway&#xff0c;archives&#xff0c;system這三個服務 三、部署步驟 四、安裝linux 五、在linux上安裝redis&#…

MPDrive:利用基于標記的提示學習提高自動駕駛的空間理解能力

25年4月來自南方科技大學、百度、英國 KCL和琶洲實驗室&#xff08;廣東 AI 和數字經濟實驗室&#xff09;的論文“MPDrive: Improving Spatial Understanding with Marker-Based Prompt Learning for Autonomous Driving”。 自動駕駛視覺問答&#xff08;AD-VQA&#xff09;…

Halcon圖像采集

Halcon是一款強大的機器視覺軟件&#xff0c;結合C#可以開發出功能完善的視覺應用程序。 基本設置 確保已經安裝了Halcon和Halcon的.NET庫&#xff08;HalconDotNet&#xff09;。 1. 添加引用 在C#項目中&#xff0c;需要添加對HalconDotNet.dll的引用&#xff1a; 右鍵點…

Win10定時任務計劃無法顯示要執行的EXE任務程序界面,問題解決辦法

用C#開發的一款WINFORM程序&#xff0c;在電腦測試一切順利&#xff0c;運行結果正確。但用電腦的定時任務執行時&#xff0c;程序界面不顯示&#xff0c;重啟電腦、各種試都不行&#xff0c;最終問題解決。 解決辦法&#xff1a; 要選“只在用戶登陸時運行”&#xff0c;才能執…

Navicat和PLSQL在oracle 使用語句報ORA-00911: 無效字符

后面我發現可能是在復制SQL語句中有中文&#xff0c;但是環境變量未配置中文環境。 因為Oracle的語法解析器特別嚴格&#xff0c;就會報出以上的錯誤出來。 SQL語句錯誤&#xff0c;存在中文字符或者sql語句空格導致&#xff0c;去掉即可解決。 我重新寫語句&#xff0c;發現…

[ctfshow web入門] web30

信息收集 題目將flag system php不區分大小寫地過濾了 解題 前置知識 print_r&#xff1a;php中用于打印數組 scandir&#xff1a;php中用于獲取指點目錄下的所以文件目錄名 getcwd&#xff1a;獲取當前目錄 目錄獲取 這里提供兩種方法 print_r(scandir(getcwd())); pri…

linux下MMC_TEST的使用

一:打開如下配置,將相關文件編譯到內核里: CONFIG_MMC_TEST CONFIG_MMC_DEBUG CONFIG_DEBUG_FS二:將mmc設備和mmc_test驅動進行綁定 2.1查看mmc設備編號 ls /sys/bus/mmc/drivers/mmcblk/mmc0:aaaa2.2將mmc設備與原先驅動進行解綁 echo mmc0:aaaa >

《深度解析LightGBM與MySQL數據集成:高效機器學習的新范式》

在機器學習工程實踐中&#xff0c;數據與模型的高效交互一直是制約算法性能發揮的關鍵瓶頸。LightGBM作為梯度提升決策樹框架的杰出代表&#xff0c;其與關系型數據庫MySQL的深度集成能力&#xff0c;為數據科學家提供了從原始數據到預測結果的完整解決方案。這種集成不是簡單的…

處理Excel的python庫openpyxl、xlrd、xlwt、pandas有什么區別,搞懂它

openpyxl、xlrd、xlwt、pandas 都能處理 Excel 表格&#xff0c;但用途和適合的場景不同。今天做個總結&#xff1a; 庫名功能支持格式讀寫支持樣式備注openpyxl全面的.xlsx處理庫.xlsx&#xff08;Excel2007&#xff09;???首選xlrd讀取.xls文件的老牌工具.xls&#xff08…

EasyExcel-一款好用的excel生成工具

EasyExcel是一款處理excel的工具類&#xff0c;主要特點如下&#xff08;官方&#xff09;&#xff1a; 特點 高性能讀寫&#xff1a;FastExcel 專注于性能優化&#xff0c;能夠高效處理大規模的 Excel 數據。相比一些傳統的 Excel 處理庫&#xff0c;它能顯著降低內存占用。…

視頻分析設備平臺EasyCVR攜手高空拋物AI智能分析技術,打造住宅小區頭頂安全智能防線

一、背景介紹 隨著城市化進程的高速推進&#xff0c;城市天際線不斷被刷新&#xff0c;高樓大廈密密麻麻。然而&#xff0c;高空拋物問題也逐漸顯現&#xff0c;這一行為不僅嚴重影響城市文明的形象&#xff0c;更帶來很多安全隱患&#xff0c;威脅居民的生命財產安全&#xf…

Spring MVC 操作會話屬性詳解(@SessionAttributes 與 @SessionAttribute)

Spring MVC 操作會話屬性詳解&#xff08;SessionAttributes 與 SessionAttribute&#xff09; 1. 核心注解對比 注解作用范圍功能SessionAttributes類級別聲明控制器中需要持久化的模型屬性&#xff08;存入 HttpSession&#xff09;SessionAttribute方法參數/返回值顯式綁定…

Python字典實戰: 三大管理系統開發指南(班級+會議+購物車)(附源碼)

目錄 摘要 一、班級管理系統&#xff08;含成績模塊&#xff09; 1. 功能概述 2. 完整代碼與解析 3. 代碼解析與亮點 二、會議管理系統 1. 功能概述 2. 完整代碼 3. 代碼解析與亮點 三、購物車管理系統 1. 功能概述 2. 完整代碼 3. 代碼解析與亮點 四、總結與擴…

北京自在科技:讓萬物接入蘋果Find My網絡的″鑰匙匠″

在AirTag掀起全球防丟熱潮的今天&#xff0c;越來越多的第三方產品開始接入蘋果Find My網絡——從充電寶到電動車&#xff0c;從行李箱到保溫杯&#xff0c;用戶只需打開iPhone的「查找」App&#xff0c;就能實時定位這些物品。 北京自在科技有限責任公司早在蘋果推出Find My開…

Vue進行前端開發流程

一、創建vue項目 創建vue項目&#xff1a;先進入要操作的目錄下&#xff0c;注意本項目是用vue2開發的。 vue create vue項目名 二、項目開發 1.創建項目結構 2.開發功能模塊 主入口App.vue <template><div class"boss-app"><Header /><m…

網絡帶寬測速工具選擇指南iperf3 nttcp tcpburn jperf使用詳解

簡介 本文主要介紹內網&#xff08;局域網&#xff09;與外網&#xff08;互聯網&#xff09;的網絡帶寬測速工具下載地址、選擇指南、參數對比、基本使用。 測速工具快速選擇指南 測速工具下載地址 iperf 官網下載鏈接&#xff1a;iperf.fr/iperf-download.php該鏈接提供了不…