Java LinkedList的實現原理圖文詳解

今天小編就爲大家分享一篇關於Java LinkedList的實現原理圖文詳解,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧

一、概述

先來看看源碼中的這一段註釋,我們先嚐試從中提取一些信息:

Doubly-linked list implementation of the List and Deque interfaces. Implements all optional list operations, and permits all elements (including null).All of the operations perform as could be expected for a doubly-linked list. Operations that index into the list will traverse the list from the beginning or the end, whichever is closer to the specified index.Note that this implementation is not synchronized. If multiple threads access a linked list concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements; merely setting the value of an element is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the list.

從這段註釋中,我們可以得知 LinkedList 是通過一個雙向鏈表來實現的,它允許插入所有元素,包括 null,同時,它是線程不同步的。如果對雙向鏈表這個數據結構很熟悉的話,學習LinkedList 就沒什麼難度了。下面是雙向鏈表的結構:

雙向鏈表每個結點除了數據域之外,還有一個前指針和後指針,分別指向前驅結點和後繼結點(如果有前驅/後繼的話)。另外,雙向鏈表還有一個 first 指針,指向頭節點,和 last 指針,指向尾節點。

二、屬性

接下來看一下 LinkedList 中的屬性:

//鏈表的節點個數
transient int size = 0;
//指向頭節點的指針
transient Node<E> first;
//指向尾節點的指針
transient Node<E> last;

LinkedList 的屬性非常少,就只有這些。通過這三個屬性,其實我們大概也可以猜測出它是怎麼實現的了。

三、方法

1、結點結構

Node 是在 LinkedList 裏定義的一個靜態內部類,它表示鏈表每個節點的結構,包括一個數據域 item,一個後置指針 next,一個前置指針 prev。

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

2、添加元素

對於鏈表這種數據結構來說,添加元素的操作無非就是在表頭/表尾插入元素,又或者在指定位置插入元素。因爲 LinkedList 有頭指針和尾指針,所以在表頭或表尾進行插入元素只需要 O(1) 的時間,而在指定位置插入元素則需要先遍歷一下鏈表,所以複雜度爲 O(n)。

在表頭添加元素的過程如下:

當向表頭插入一個節點時,很顯然當前節點的前驅一定爲 null,而後繼結點是 first 指針指向的節點,當然還要修改 first 指針指向新的頭節點。除此之外,原來的頭節點變成了第二個節點,所以還要修改原來頭節點的前驅指針,使它指向表頭節點,源碼的實現如下:

private void linkFirst(E e) {
 final Node<E> f = first;
 //當前節點的前驅指向 null,後繼指針原來的頭節點
 final Node<E> newNode = new Node<>(null, e, f);
 //頭指針指向新的頭節點
 first = newNode;
 //如果原來有頭節點,則更新原來節點的前驅指針,否則更新尾指針
 if (f == null)
 last = newNode;
 else
 f.prev = newNode;
 size++;
 modCount++;
}

在表尾添加元素跟在表頭添加元素大同小異,如圖所示

當向表尾插入一個節點時,很顯然當前節點的後繼一定爲 null,而前驅結點是 last指針指向的節點,然後還要修改 last 指針指向新的尾節點。此外,還要修改原來尾節點的後繼指針,使它指向新的尾節點,源碼的實現如下:

void linkLast(E e) {
 final Node<E> l = last;
 //當前節點的前驅指向尾節點,後繼指向 null
 final Node<E> newNode = new Node<>(l, e, null);
 //尾指針指向新的尾節點
 last = newNode;
 //如果原來有尾節點,則更新原來節點的後繼指針,否則更新頭指針
 if (l == null)
 first = newNode;
 else
 l.next = newNode;
 size++;
 modCount++;
}

最後,在指定節點之前插入,如圖所示

當向指定節點之前插入一個節點時,當前節點的後繼爲指定節點,而前驅結點爲指定節點的前驅節點。此外,還要修改前驅節點的後繼爲當前節點,以及後繼節點的前驅爲當前節點,源碼的實現如下:

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

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對神馬文庫的支持。如果你想了解更多相關內容請查看下面相關鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章