日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

LinkedList源码分析(基于Java8)

發布時間:2025/3/19 java 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LinkedList源码分析(基于Java8) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • LinkedList是一個實現了List接口和Deque接口的雙端鏈表
  • 有關索引的操作可能從鏈表頭開始遍歷到鏈表尾部,也可能從尾部遍歷到鏈表頭部,這取決于看索引更靠近哪一端。
  • LinkedList不是線程安全的,如果想使LinkedList變成線程安全的,可以使用如下方式:
List list=Collections.synchronizedList(new LinkedList(...));

iterator()和listIterator()返回的迭代器都遵循fail-fast機制。



從上圖可以看出LinkedList與ArrayList的不同之處

  • ArrayList直接繼承自AbstractList
  • LinkedList繼承自AbstractSequentialList,然后再繼承自AbstractList。另外還實現了Dequeu接口,雙端隊列。

內部結構

LinkedList內部是一個雙端鏈表的結構


LinkedList的結構

從上圖可以看出,LinkedList內部是一個雙端鏈表結構,有兩個變量,first指向鏈表頭部,last指向鏈表尾部。


成員變量
size表示當前鏈表中的數據個數

下面是Node節點的定義,LinkedList的靜態內部類



從Node的定義可以看出鏈表是一個雙端鏈表的結構。

構造方法

LinkedList有兩個構造方法,一個用于構造一個空的鏈表,一個用已有的集合創建鏈表


添加

因為LinkedList即實現了List接口,又實現了Deque,所以LinkedList既可以添加將元素添加到尾部,也可以將元素添加到指定索引位置,還可以添加添加整個集合;另外既可以在頭部添加,又可以在尾部添加。

分別從List接口和Deque接口介紹。

List接口的添加

add(E e)

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;else//鏈表不為空,那么將該結點作為原鏈表尾部的后繼節點l.next = newNode;size++;//增加尺寸modCount++; }

從上面代碼可以看到,就是一個鏈表尾部添加一個雙端節點的操作,但是需要注意對鏈表為空時頭節點的處理。

add(int index,E e)

add(int index,E e)用于在指定位置添加元素



1. 檢查index的范圍,否則拋出異常
2. 如果插入位置是鏈表尾部,那么調用linkLast方
3. 如果插入位置是鏈表中間,那么調用linkBefore

看一下linkBefore的實現
在看linkBefore之前,先看一下node(int index)方法,該方法返回指定位置的節點


node(int index)將根據index是靠近頭部還是尾部選擇不同的遍歷方向
一旦得到了指定索引位置的節點,再看linkBefore()

linkBefore()方法在第二個參數節點前插入一個新節點
linkBefore#第一步
linkBefore#第二步
linkBefore#第三步
linkBefore主要分三步
1. 創建newNode節點,將newNode的后繼指針指向succ,前驅指針指向pred
2. 將succ的前驅指針指向newNode
3. 根據pred是否為null,進行不同操作。

  • 如果pred為null,說明該節點插入在頭節點之前,要重置first頭節點
  • 如果pred不為null,那么直接將pred的后繼指針指向newNode即可

addAll

addAll有兩個重載方法

  • 一個參數的方法表示將集合元素添加到鏈表尾部
  • 兩個參數的方法指定了開始插入的位置
//將集合插入到鏈表尾部,即開始索引位置為size public boolean addAll(Collection<? extends E> c) {return addAll(size, c);}//將集合從指定位置開始插入 public boolean addAll(int index, Collection<? extends E> c) {//Step 1:檢查index范圍checkPositionIndex(index);//Step 2:得到集合的數據Object[] a = c.toArray();int numNew = a.length;if (numNew == 0)return false;//Step 3:得到插入位置的前驅節點和后繼節點Node<E> pred, succ;//如果插入位置為尾部,前驅節點為last,后繼節點為nullif (index == size) {succ = null;pred = last;}//否則,調用node()方法得到后繼節點,再得到前驅節點else {succ = node(index);pred = succ.prev;}//Step 4:遍歷數據將數據插入for (Object o : a) {@SuppressWarnings("unchecked") E e = (E) o;//創建新節點Node<E> newNode = new Node<>(pred, e, null);//如果插入位置在鏈表頭部if (pred == null)first = newNode;elsepred.next = newNode;pred = newNode;}//如果插入位置在尾部,重置last節點if (succ == null) {last = pred;}//否則,將插入的鏈表與先前鏈表連接起來else {pred.next = succ;succ.prev = pred;}size += numNew;modCount++;return true;}

1. 檢查index索引范圍
2. 得到集合數據
3. 得到插入位置的前驅和后繼節點
4. 遍歷數據,將數據插入到指定位置

Deque接口的添加

addFirst(E e)

將元素添加到鏈表頭部

public void addFirst(E e) {linkFirst(e);}private void linkFirst(E e) {final Node<E> f = first;final Node<E> newNode = new Node<>(null, e, f);//新建節點,以頭節點為后繼節點first = newNode;//如果鏈表為空,last節點也指向該節點if (f == null)last = newNode;//否則,將頭節點的前驅指針指向新節點elsef.prev = newNode;size++;modCount++;}

在頭節點插入一個節點使新節點成為新節點,但是和linkLast一樣需要注意當鏈表為空時,對last節點的設置

addLast(E e)

將元素添加到鏈表尾部,與add()方法一樣

public void addLast(E e) {linkLast(e); }

offer(E e)

將數據添加到鏈表尾部,其內部調用了add(E e)方法

public boolean offer(E e) {return add(e); }

offerFirst(E e)方法

將數據插入鏈表頭部,與addFirst的區別在于

  • 該方法可以返回特定的返回值
  • addFirst的返回值為void。
public boolean offerFirst(E e) {addFirst(e);return true; }

offerLast(E e)方法

offerLast()與addLast()的區別和offerFirst()和addFirst()的區別一樣

添加操作總結

LinkedList由于實現了List和Deque接口,所以有多種添加方法,總結一下

  • 將數據插入到鏈表尾部
    • boolean add(E e):
    • void addLast(E e)
    • boolean offerLast(E e)
  • 將數據插入到鏈表頭部
    • void addFirst(E e)
    • boolean offerFirst(E e)
  • 將數據插入到指定索引位置
    • boolean add(int index,E e)

2檢索

2.1 根據位置取數據

2.1.1 get(int index)

獲取任意位置的,get(int index)方法根據指定索引返回數據,如果索引越界,那么會拋出異常

/*** Returns the element at the specified position in this list.** @param index index of the element to return* @return the element at the specified position in this list* @throws IndexOutOfBoundsException {@inheritDoc}*/public E get(int index) {checkElementIndex(index);return node(index).item;}

1.檢查index邊界,index>=0&&index
2.返回指定索引位置的元素

2.1.2 獲得位置為0的頭節點數據

LinkedList中有多種方法可以獲得頭節點的數據,區別在于對鏈表為空時的處理,是拋異常還是返回null
主要方法有getFirst()、element()、peek()、peekFirst()
其中getFirst()和element()方法將會在鏈表為空時,拋出異常

/*** Returns the first element in this list.** @return the first element in this list* @throws NoSuchElementException if this list is empty*/public E getFirst() {final Node<E> f = first;if (f == null)throw new NoSuchElementException();return f.item;} /*** Retrieves, but does not remove, the head (first element) of this list.** @return the head of this list* @throws NoSuchElementException if this list is empty* @since 1.5*/public E element() {return getFirst();}

從代碼可以看到,element()方法的內部就是使用getFirst()實現的。它們會在鏈表為空時,拋NoSuchElementException
下面再看peek()和peekFirst()

/*** Retrieves, but does not remove, the head (first element) of this list.** @return the head of this list, or {@code null} if this list is empty* @since 1.5*/public E peek() {final Node<E> f = first;return (f == null) ? null : f.item;} /*** Retrieves, but does not remove, the first element of this list,* or returns {@code null} if this list is empty.** @return the first element of this list, or {@code null}* if this list is empty* @since 1.6*/public E peekFirst() {final Node<E> f = first;return (f == null) ? null : f.item;}

當鏈表為空時,peek()和peekFirst()方法返回null

2.1.3 獲得位置為size-1的尾節點數據

獲得尾節點數據的方法有

  • getLast()
/*** Returns the last element in this list.** @return the last element in this list* @throws NoSuchElementException if this list is empty*/public E getLast() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return l.item;}

getLast()在鏈表為空時,會拋NoSuchElementException,

  • peekLast()
    只是會返回null
    peekLast()
/*** Retrieves, but does not remove, the last element of this list,* or returns {@code null} if this list is empty.** @return the last element of this list, or {@code null}* if this list is empty* @since 1.6*/public E peekLast() {final Node<E> l = last;return (l == null) ? null : l.item;}

2.2 根據對象得到索引

  • 第一個匹配的索引
    從前往后遍歷
  • 最后一個匹配的索引
    從后往前遍歷

2.2.1 indexOf()

/*** 返回第一個匹配的索引* in this list, or -1 if this list does not contain the element.* More formally, returns the lowest index {@code i} such that* <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,* or -1 if there is no such index.** @param o element to search for* @return the index of the first occurrence of the specified element in* this list, or -1 if this list does not contain the element*/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;}

可以看到,LinkedList可包含null,遍歷方式都是從前往后,一旦匹配了,就返回索引

2.2.2 lastIndexOf()

返回最后一個匹配的索引,實現為從后往前遍歷

/*** Returns the index of the last occurrence of the specified element* in this list, or -1 if this list does not contain the element.* More formally, returns the highest index {@code i} such that* <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,* or -1 if there is no such index.** @param o element to search for* @return the index of the last occurrence of the specified element in* this list, or -1 if this list does not contain the element*/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;}

2.3 檢查是否包含某對象

contains(Object o)
檢查對象o是否存在于鏈表中

/*** Returns {@code true} if this list contains the specified element.* More formally, returns {@code true} if and only if this list contains* at least one element {@code e} such that* <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.** @param o element whose presence in this list is to be tested* @return {@code true} if this list contains the specified element*/public boolean contains(Object o) {return indexOf(o) != -1;}

從代碼可以看到contains()方法調用了indexOf()方法,只要返回結果不是-1,那就說明該對象存在于鏈表中

2.4 檢索操作總結

檢索操作分為按照位置得到對象以及按照對象得到位置兩種方式,其中按照對象得到位置的方法有indexOf()和lastIndexOf();按照位置得到對象有如下方法:

  • 根據任意位置得到數據的get(int index)方法,當index越界會拋出異常
  • 獲得頭節點數據
  • getFirst()和element()方法在鏈表為空時會拋出NoSuchElementException
  • peek()和peekFirst()方法在鏈表為空時會返回null
  • 獲得尾節點數據
  • getLast()在鏈表為空時會拋出NoSuchElementException
  • peekLast()在鏈表為空時會返回null

3刪除

  • 按照位置刪除
    • 返回是否刪除成功的標志
    • 返回被刪除的元素
  • 按照對象刪除

3.1 刪除指定對象

remove(Object o)
一次只刪除一個匹配的對象,如果刪除了匹配對象,返回true,否則false

/*** Removes the first occurrence of the specified element from this list,* if it is present. If this list does not contain the element, it is* unchanged. More formally, removes the element with the lowest index* {@code i} such that* <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>* (if such an element exists). Returns {@code true} if this list* contained the specified element (or equivalently, if this list* changed as a result of the call).** @param o element to be removed from this list, if present* @return {@code true} if this list contained the specified element*/public boolean remove(Object o) {if (o == null) {for (Node<E> x = first; x != null; x = x.next) {//一旦匹配,調用unlink()方法和返回trueif (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;}

由于LinkedList可以存儲null,所以對刪除對象以是否為null做區分
然后從鏈表頭開始遍歷,一旦匹配,就會調用unlink()方法將該節點從鏈表中移除

下面是unlink()

/*** Unlinks non-null node x.*/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;} unlink第一步

第一步:得到待刪除節點的前驅節點和后繼節點


unlink第二步

第二步:刪除前驅節點


unlink第三步

第三步:刪除后繼節點
經過三步,待刪除的結點就從鏈表中脫離了。需要注意的是刪除位置是頭節點或尾節點時候的處理,上面的示意圖沒有特別指出。

3.2 按照位置刪除對象

3.2.1 刪除任意位置的對象

  • boolean remove(int index)
    刪除任意位置的元素,刪除成功將返回true,否則false
/*** Removes the element at the specified position in this list. Shifts any* subsequent elements to the left (subtracts one from their indices).* Returns the element that was removed from the list.** @param index the index of the element to be removed* @return the element previously at the specified position* @throws IndexOutOfBoundsException {@inheritDoc}*/public E remove(int index) {checkElementIndex(index);return unlink(node(index));}

1. 檢查index范圍,屬于[0,size)
2. 將索引出節點刪除

3.2.2 刪除頭節點的對象

  • remove()、removeFirst()、pop()
    在鏈表為空時將拋出NoSuchElementException
/*** Retrieves and removes the head (first element) of this list.** @return the head of this list* @throws NoSuchElementException if this list is empty* @since 1.5*/public E remove() {return removeFirst();}/*** Pops an element from the stack represented by this list. In other* words, removes and returns the first element of this list.** <p>This method is equivalent to {@link #removeFirst()}.** @return the element at the front of this list (which is the top* of the stack represented by this list)* @throws NoSuchElementException if this list is empty* @since 1.6*/public E pop() {return removeFirst();}/*** Removes and returns the first element from this list.** @return the first element from this list* @throws NoSuchElementException if this list is empty*/public E removeFirst() {final Node<E> f = first;if (f == null)throw new NoSuchElementException();return unlinkFirst(f);}

remove()和pop()內部調用了removeFirst()
而removeFirst()在鏈表為空時將拋出NoSuchElementException

  • poll()和,pollFirst()
    在鏈表為空時將返回null
/*** Retrieves and removes the head (first element) of this list.** @return the head of this list, or {@code null} if this list is empty* @since 1.5*/public E poll() {final Node<E> f = first;return (f == null) ? null : unlinkFirst(f);}/*** Retrieves and removes the first element of this list,* or returns {@code null} if this list is empty.** @return the first element of this list, or {@code null} if* this list is empty* @since 1.6*/public E pollFirst() {final Node<E> f = first;return (f == null) ? null : unlinkFirst(f);}

poll()和pollFirst()的實現代碼是相同的,在鏈表為空時將返回null

3.2.3 刪除尾節點的對象

  • removeLast()
/*** Removes and returns the last element from this list.** @return the last element from this list* @throws NoSuchElementException if this list is empty*/public E removeLast() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return unlinkLast(l);}

可以看到removeLast()在鏈表為空時將拋出NoSuchElementException

  • pollLast()
/*** Retrieves and removes the last element of this list,* or returns {@code null} if this list is empty.** @return the last element of this list, or {@code null} if* this list is empty* @since 1.6*/public E pollLast() {final Node<E> l = last;return (l == null) ? null : unlinkLast(l);}

可以看到pollLast()在鏈表為空時會返回null,而不是拋出異常

3.3 刪除操作總結

刪除操作由很多種方法

按照指定對象刪除

boolean remove(Object o),一次只會刪除一個匹配的對象

按照指定位置刪除

  • 刪除任意位置的對象
    E remove(int index),當index越界時會拋出異常
  • 刪除頭節點位置的對象
    • 在鏈表為空時拋出異常
      E remove()、E removeFirst()、E pop()
    • 在鏈表為空時返回null
      E poll()、E pollFirst()
  • 刪除尾節點位置的對象
    • 在鏈表為空時拋出異常
      E removeLast()
    • 在鏈表為空時返回null
      E pollLast()

4迭代器

LinkedList的iterator()內部調用了其listIterator()方法,所以可只分析listIterator()方法listIterator()提供了兩個重載方法。

iterator()方法和listIterator()方法的關系如下

public Iterator<E> iterator() {return listIterator();}public ListIterator<E> listIterator() {return listIterator(0);}public ListIterator<E> listIterator(int index) {checkPositionIndex(index);return new ListItr(index);}

從上面可以看到三者的關系是iterator()——>listIterator(0)——>listIterator(int index)
最終都會調用listIterator(int index),其中參數表示迭代器開始的位置
ListIterator是一個可以指定任意位置開始迭代,并且有兩個遍歷方法

下面直接看ListItr

private class ListItr implements ListIterator<E> {private Node<E> lastReturned;private Node<E> next;private int nextIndex;private int expectedModCount = modCount;//保存當前modCount,確保fail-fast機制ListItr(int index) {// assert isPositionIndex(index);next = (index == size) ? null : node(index);//得到當前索引指向的next節點nextIndex = index;}public boolean hasNext() {return nextIndex < size;}//獲取下一個節點public E next() {checkForComodification();if (!hasNext())throw new NoSuchElementException();lastReturned = next;next = next.next;nextIndex++;return lastReturned.item;}public boolean hasPrevious() {return nextIndex > 0;}//獲取前一個節點,將next節點向前移public E previous() {checkForComodification();if (!hasPrevious())throw new NoSuchElementException();lastReturned = next = (next == null) ? last : next.prev;nextIndex--;return lastReturned.item;}public int nextIndex() {return nextIndex;}public int previousIndex() {return nextIndex - 1;}public void remove() {checkForComodification();if (lastReturned == null)throw new IllegalStateException();Node<E> lastNext = lastReturned.next;unlink(lastReturned);if (next == lastReturned)next = lastNext;elsenextIndex--;lastReturned = null;expectedModCount++;}public void set(E e) {if (lastReturned == null)throw new IllegalStateException();checkForComodification();lastReturned.item = e;}public void add(E e) {checkForComodification();lastReturned = null;if (next == null)linkLast(e);elselinkBefore(e, next);nextIndex++;expectedModCount++;}public void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);while (modCount == expectedModCount && nextIndex < size) {action.accept(next.item);lastReturned = next;next = next.next;nextIndex++;}checkForComodification();}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}

構造器中,得到了當前位置的節點,就是變量next
next()返回當前節點的值并將next指向其后繼節點
previous()返回當前節點的前一個節點的值并將next節點指向其前驅節點
由于Node是一個雙端節點,所以這兒用了一個節點就可以實現從前向后迭代和從后向前迭代
另外在ListIterator初始時,exceptedModCount保存了當前的modCount,如果在迭代期間,有操作改變了鏈表的底層結構,那么再操作迭代器的方法時將會拋出ConcurrentModificationException。

5 例子

由于LinkedList是一個實現了Deque的雙端隊列,所以LinkedList既可以當做Queue,又可以當做Stack,下面的例子將LinkedList成Stack

public class LinkedStack<E> {private LinkedList<E> linkedList;public LinkedStack() {linkedList = new LinkedList<E>();}//壓入數據public void push(E e) {linkedList.push(e);}//彈出數據,在Stack為空時將拋出異常public E pop() {return linkedList.pop();}//檢索棧頂數據,但是不刪除public E peek() {return linkedList.peek();}}

在將LinkedList當做Stack時,使用pop()、push()、peek()方法需要注意的是LinkedList內部是將鏈表頭部當做棧頂,鏈表尾部當做棧底,也就意味著所有的壓入、攤入操作都在鏈表頭部進行

6總結

LinkedList是基于雙端鏈表的List,其內部的實現源于對鏈表的操作

  • 適用于頻繁增加、刪除的情況
  • 該類不是線程安全的
  • 由于LinkedList實現了Queue接口,所以LinkedList不止有隊列的接口,還有棧的接口,可以使用LinkedList作為隊列和棧的實現

總結

以上是生活随笔為你收集整理的LinkedList源码分析(基于Java8)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。