Java中的LinkedList底層源碼分析
一. 基本原理和優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1.底層基于雙向鏈表,往LinkedList中間插入元素時(shí),不需要移動大量的元素,只需要修改前后節(jié)點(diǎn)的指針,速度快。
2.適合頻繁、大量的插入元素,不會導(dǎo)致頻繁的擴(kuò)容和拷貝元素。插入元素,只不過就是把新的元素掛到舊元素下面。
缺點(diǎn):
1.不適合讀取隨機(jī)位置的元素,比如list.get(10),因?yàn)樾枰闅v鏈表,直到找到這個位置上的元素為止。
二. 源碼分析
2.1 add
默認(rèn)在雙向鏈表的尾部插入一個元素
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;
else
l.next = newNode;
size++;
modCount++;
}2.2 node
在雙向鏈表中,找到目標(biāo)下標(biāo)對應(yīng)的節(jié)點(diǎn)。這里可以學(xué)習(xí)鏈表的遍歷方式。
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;
}
}首先,通過index < (size >> 1),判斷待尋找的節(jié)點(diǎn)在雙向鏈表的前半部分,還是后半部分。
如果是前半部分,則會從雙向鏈表的頭節(jié)點(diǎn)開始遍歷,通過節(jié)點(diǎn)的next指針,不斷的向后尋找節(jié)點(diǎn)。→
如果是后半部分,則會從雙向鏈表的尾節(jié)點(diǎn)開始遍歷,通過節(jié)點(diǎn)的prev指針,不斷的向前尋找節(jié)點(diǎn)。←
2.3 add(int index, E element)
在指定元素的前面,插入一個元素。比如現(xiàn)在想要在隊(duì)尾插入一個元素。
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}succ指向隊(duì)尾元素,succ.prev表示隊(duì)尾節(jié)點(diǎn)前面的那個節(jié)點(diǎn)(倒數(shù)第二個節(jié)點(diǎn))。
首先,創(chuàng)建一個新節(jié)點(diǎn),讓新節(jié)點(diǎn)的prev指針指向倒數(shù)第二個節(jié)點(diǎn),新節(jié)點(diǎn)的next指針指向隊(duì)尾節(jié)點(diǎn)。
接著,讓隊(duì)尾節(jié)點(diǎn)指的prev指向新節(jié)點(diǎn)。
最后,把倒數(shù)第二個節(jié)點(diǎn)的next指針指向新節(jié)點(diǎn)。
2.4 get
獲取一個隨機(jī)位置的節(jié)點(diǎn)中的值。
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}隨機(jī)讀取是ArrayList的強(qiáng)項(xiàng), 因?yàn)锳rrayList底層基于數(shù)組實(shí)現(xiàn),通過下標(biāo)能快速找到對應(yīng)的內(nèi)存地址,接著直接讀取內(nèi)存地址中的值。
隨機(jī)讀取是LinkedList弱項(xiàng),LinkedList底層基于雙向鏈表實(shí)現(xiàn),它沒辦法通過下標(biāo),直接找到內(nèi)存地址,必須從頭、尾節(jié)點(diǎn)開始,借助節(jié)點(diǎn)的prev和next指針,不斷的向前或向后尋找,直到找到元素為止。
node()方法的代碼之前已經(jīng)學(xué)習(xí)過了,先大致的判斷目標(biāo)節(jié)點(diǎn)距離頭、尾節(jié)點(diǎn),哪個節(jié)點(diǎn)更近,盡量的減少查詢的次數(shù),接著就是借助頭尾節(jié)點(diǎn),不斷的向前或向后找,比如從頭節(jié)點(diǎn),不斷的向后找。
2.5 getFirst
返回頭節(jié)點(diǎn)的值。如果頭節(jié)點(diǎn)為空,則拋出異常。
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}2.6 peek
返回頭節(jié)點(diǎn)的值,頭節(jié)點(diǎn)不需要出隊(duì)。
peek與getFirst的區(qū)別:如果不存在頭節(jié)點(diǎn),peek會返回null,而getFirst會直接報(bào)錯。
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}2.7 getLast
返回尾部節(jié)點(diǎn)的值。
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}2.8 removeLast
刪除隊(duì)尾節(jié)點(diǎn)。
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}return xprivate E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
};通過last指針找到隊(duì)尾元素,斷開隊(duì)尾元素與倒數(shù)第二個元素的指針指向,last指針指向倒數(shù)第二個元素,作為新的隊(duì)尾元素。
此時(shí),原隊(duì)尾節(jié)點(diǎn)的next指針等于null,item等于null,prev等于null,且沒有被任何節(jié)點(diǎn)指向,接著就靠JVM來進(jìn)行垃圾回收了。
2.9 removeFirst
刪除隊(duì)頭節(jié)點(diǎn)。
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}把隊(duì)列的頭節(jié)點(diǎn)與第二個節(jié)點(diǎn)之前的指針指向全部斷開,讓JVM來回收頭節(jié)點(diǎn)。
接著,讓first指針原隊(duì)列的第二個節(jié)點(diǎn),作為隊(duì)列新的頭結(jié)點(diǎn)。
2.10 remove(int index)
刪除指定下標(biāo)的節(jié)點(diǎn)。
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
E unlink(Node<E> x) {
// assert x != null;
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;
}首先,通過node方法,遍歷鏈表,找到待刪除的節(jié)點(diǎn)。
接著,解除待刪除節(jié)點(diǎn),對于左右兩邊節(jié)點(diǎn)的所有指針指向。讓左右兩邊的節(jié)點(diǎn)的next和prev指針互相指向。
最后,由JVM回收這個沒有任何人指向的節(jié)點(diǎn)。
三. 總結(jié)
LinkedList是一個基于雙向鏈表實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu),對于隊(duì)頭和隊(duì)尾節(jié)點(diǎn)來說,無論是插入、刪除還是讀取節(jié)點(diǎn)的值,其實(shí)都是很輕松的。并且,默認(rèn)從隊(duì)尾插入節(jié)點(diǎn),從隊(duì)頭獲取節(jié)點(diǎn),所以LinkedList天然就可以作為隊(duì)列來使用。
由于基于雙向鏈表實(shí)現(xiàn),所以無論你怎么插入數(shù)據(jù),LinkedList的性能都很不錯,不用擔(dān)心擴(kuò)容,移動大量元素等問題,性能上很好。
但是呢,在鏈表的中間插入元素,比在隊(duì)頭和隊(duì)尾插入元素的性能要差一些,這是因?yàn)殛?duì)頭和隊(duì)尾分別有first和last指針指向著它們,如果要在鏈表的中間指定位置插入元素,首先要遍歷鏈表,找到目標(biāo)元素,然后才能修改左右兩邊節(jié)點(diǎn)的指針,插入節(jié)點(diǎn)。
此外,如果要隨機(jī)獲取某個位置的元素,尤其是鏈表內(nèi)節(jié)點(diǎn)的數(shù)量很多的時(shí)候,由于需要遍歷鏈表,所以性能比較差。
到此這篇關(guān)于Java中的LinkedList底層源碼分析的文章就介紹到這了,更多相關(guān)LinkedList底層源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
POI讀取excel簡介_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了POI讀取excel簡介,詳細(xì)的介紹了什么是Apache POI和組件,有興趣的可以了解了解一下2017-08-08
Java實(shí)戰(zhàn)網(wǎng)上電子書城的實(shí)現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+JSP+maven+Mysql實(shí)現(xiàn)一個網(wǎng)上電子書城,大家可以在過程中查缺補(bǔ)漏,提升水平2022-01-01
Spring?Security自定義AuthenticationManager實(shí)現(xiàn)手機(jī)號/密碼雙認(rèn)證
這篇文章給大家介紹了Spring?Security自定義AuthenticationManager實(shí)現(xiàn)手機(jī)號/密碼雙認(rèn)證,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2026-06-06
SpringBoot Maven打包如何根據(jù)環(huán)境排除文件
文章介紹了在SpringBoot項(xiàng)目中,根據(jù)不同的環(huán)境(開發(fā)、測試、生產(chǎn))進(jìn)行JSP文件打包處理的方法,通過配置`pom.xml`文件中的``標(biāo)簽,可以實(shí)現(xiàn)開發(fā)環(huán)境保留`index.jsp`文件,測試環(huán)境和生產(chǎn)環(huán)境排除該文件2024-12-12

