概述
LinkedList底層是基於鏈表實現。鏈表沒有長度限制,內存地址不需要固定長度,也不需要是連續的地址來進行存儲,只需要通過引用來關聯前後元素即可完成整個鏈表的連續。所以鏈表的優點就是添加刪除元素比較快,只需要移動指針,並且不需要判斷擴容。缺點就是因爲沒有索引,所以在查詢和遍歷元素時候比較慢。
使用場景:在增刪操作使用較多,查詢遍歷操作使用較少情況下比較適合去使用;例如:拿來當棧使用。
數據結構
繼承實現關係
1 public class LinkedList<E>
2 extends AbstractSequentialList<E>
3 implements List<E>, Deque<E>, Cloneable, java.io.Serializable
-
AbstractSequentialList
:本質上面與繼承AbstractList
沒有什麼區別,AbstractSequentialList
完善了AbstractList
中沒有實現的方法。 -
List
:實現List接口。 -
Deque
:實現Deque隊列接口,擁有作爲雙端隊列(隊列和棧)的功能。 -
Cloneable
:重寫clone()方法,通過創建新的LinkedList 對象,遍歷拷貝數據進行對象拷貝。 -
Serializable
:重寫read/writeObject() 方法實現序列化。
靜態內部類
爲什麼Node這個類是靜態的?答案是:這跟內存泄露有關,Node類是在LinkedList類中的,也就是一個內部類,若不使用static修飾,那麼Node就是一個普通的內部類,在java中,一個普通內部類在實例化之後,默認會持有外部類的引用,這就有可能造成內存泄露(內部類與外部類生命週期不一致時)。但使用static修飾過的內部類(稱爲靜態內部類),就不會有這種問題。【獲取資料】
- 非靜態內部類會自動生成一個構造器依賴於外部類:也是內部類可以訪問外部類的實例變量的原因。
- 靜態內部類不會生成,訪問不了外部類的實例變量,只能訪問類變量。
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 transient int size = 0;
3 // 頭結點
4 transient Node<E> first;
5 // 尾節點
6 transient Node<E> last;
構造方法
1 public LinkedList() {
2 }
3
4 public LinkedList(Collection<? extends E> c) {
5 this();
6 addAll(c);
7 }
主要方法解析
獲取元素
peek():隊列的查,獲取隊頭元素。
public E get(int index) { // 根據索引獲取
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
// 利用二分法查找;小於中間數從頭結點開始找,否則從尾節點開始找
//加入Java開發交流君樣:756584822一起吹水聊天
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;
}
}
// 獲取隊頭元素 ,但是不刪除隊列的頭元素(雙端隊列Deque中的方法)
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
// 獲取隊尾元素,但是不刪除隊列的尾元素(實現雙端隊列Deque中的方法)
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
// 隊列的查。獲取隊頭元素。
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
添加元素
offer():隊列的增,添加隊尾元素,底層實現是調用add()->linkLast()。
push():棧的增,把元素壓入棧中,添加對頭元素,底層實現是調用addFirst()->linkFirst()。
1 // 添加元素,默認在鏈表尾部添加
2 public boolean add(E e) {
3 linkLast(e);
4 return true;
5 }//加入Java開發交流君樣:756584822一起吹水聊天
6 // 指定索引添加元素
7 public void add(int index, E element) {
8 checkPositionIndex(index); // 檢驗下標是否越界
9
10 if (index == size) // 如果要插入的索引等於現有元素長度,說明是要在尾部插入元素
11 linkLast(element);
12 else // 否則,獲取該索引節點,在該節點之前插入元素
13 linkBefore(element, node(index));
14 }
15
16 public boolean addAll(Collection<? extends E> c) {
17 return addAll(size, c);
18 }
19 public boolean addAll(int index, Collection<? extends E> c) {
20 checkPositionIndex(index); // 檢驗下標是否越界
21
22 Object[] a = c.toArray();
23 int numNew = a.length;
24 if (numNew == 0)
25 return false;
26
27 // pred:前置節點,在該節點之後插入元素。succ:該索引位節點。
28 Node<E> pred, succ;
29 if (index == size) {
30 succ = null;
31 pred = last;
32 } else {
33 succ = node(index);
34 pred = succ.prev;
35 }
36 // 將數組設置爲鏈表
37 for (Object o : a) {
38 @SuppressWarnings("unchecked") E e = (E) o;
39 Node<E> newNode = new Node<>(pred, e, null); // 新建一個節點,指向前置節點
40 if (pred == null) // 如果前置節點爲空,說明該鏈表爲空。將頭節點指向當前節點
41 first = newNode;
42 else // 前置節點的後繼節點指向當前節點
43 pred.next = newNode;
44 pred = newNode; // 將當前節點設置爲前置節點,供後面需要插入的節點使用
45 }//加入Java開發交流君樣:756584822一起吹水聊天
46
47 if (succ == null) {
48 last = pred;
49 } else {
50 pred.next = succ;
51 succ.prev = pred;
52 }
53
54 size += numNew;
55 modCount++;
56 return true;
57 }
58
59 // 添加元素到頭結點(實現雙端隊列Deque中的方法)
60 public void addFirst(E e) {
61 linkFirst(e);
62 }
63 private void linkFirst(E e) {
64 final Node<E> f = first; // 原頭節點
65 final Node<E> newNode = new Node<>(null, e, f); // 新建一個節點,要添加的元素
66 first = newNode; // 將頭結點指向該新建的節點
67 if (f == null) // 如果原頭結點爲空,說明原鏈表爲空。這是添加的第一個元素,將尾結點也指向該新建的節點
68 last = newNode;
69 else // 如果原頭結點不爲空,則將原頭結點的前置節點指向該新建的節點
70 f.prev = newNode;
71 size++; // 元素數量+1
72 modCount++; // 修改次數+1
73 }
74 // 添加元素到尾結點(實現雙端隊列Deque中的方法)
75 public void addLast(E e) {
76 linkLast(e);
77 }
78 void linkLast(E e) {
79 final Node<E> l = last; // 原尾結點
80 final Node<E> newNode = new Node<>(l, e, null); // 新建一個節點,要添加的元素
81 last = newNode; // 將尾結點指向該新建的節點
82 if (l == null) // 如果尾頭結點爲空,說明原鏈表爲空。這是添加的第一個元素,將頭結點也指向該新建的節點
83 first = newNode;
84 else // 如果原尾結點不爲空,則將原尾結點的後繼節點指向該新建的節點
85 l.next = newNode;
86 size++; // 元素數量+1
87 modCount++; // 修改次數+1
88 }
89
90 // 隊列的添加方法
91 public boolean offer(E e) {
92 return add(e);
93 }
94
95 // 棧的添加方法
96 public void push(E e) {
97 addFirst(e);
98 }
刪除元素
poll():隊列的刪,獲取對頭元素並且對頭元素刪除。
pop():棧的刪,返回的是棧頂元素並將棧頂元素刪除。
1 public boolean remove(Object o) {
2 if (o == null) {
3 for (Node<E> x = first; x != null; x = x.next) {
4 if (x.item == null) {
5 unlink(x);
6 return true;
7 }
8 }
9 } else {
10 for (Node<E> x = first; x != null; x = x.next) {
11 if (o.equals(x.item)) {
12 unlink(x);
13 return true;
14 }
15 }
16 }
17 return false;
18 }
19 public E remove(int index) {
20 checkElementIndex(index);
21 return unlink(node(index));
22 }
23 // 將該節點前置節點的下一個節點指向該節點後繼節點,將該節點後繼節點的上一個節點指向該節點前置節點。並將該節點置爲空
24 E unlink(Node<E> x) {
25 // assert x != null;
26 final E element = x.item;
27 final Node<E> next = x.next;
28 final Node<E> prev = x.prev;
29
30 if (prev == null) {
31 first = next;
32 } else {
33 prev.next = next;
34 x.prev = null;
35 }
36
37 if (next == null) {
38 last = prev;
39 } else {
40 next.prev = prev;
41 x.next = null;
42 }
43
44 x.item = null;
45 size--;
46 modCount++;
47 return element;
48 }
49 // 將頭結點的下一個節點設置爲新的頭結點,並將原頭節點置爲空
50 private E unlinkFirst(Node<E> f) {
51 // assert f == first && f != null;
52 final E element = f.item;
53 final Node<E> next = f.next;
54 f.item = null;
55 f.next = null; // help GC
56 first = next;
57 if (next == null)
58 last = null;
59 else
60 next.prev = null;
61 size--;
62 modCount++;
63 return element;
64 }
65 //加入Java開發交流君樣:756584822一起吹水聊天
66 // 將尾結點的上一個節點設置爲新的尾結點,並將原尾節點置爲空
67 private E unlinkLast(Node<E> l) {
68 // assert l == last && l != null;
69 final E element = l.item;
70 final Node<E> prev = l.prev;
71 l.item = null;
72 l.prev = null; // help GC
73 last = prev;
74 if (prev == null)
75 first = null;
76 else
77 prev.next = null;
78 size--;
79 modCount++;
80 return element;
81 }
82
83 public E remove() {
84 return removeFirst();
85 }
86
87 // 隊列的刪除方法
88 public E poll() {
89 final Node<E> f = first;
90 return (f == null) ? null : unlinkFirst(f);
91 }
92 // 棧的刪除方法
93 public E pop() {
94 return removeFirst();
95 }
最後,祝大家早日學有所成,拿到滿意offer,快速升職加薪,走上人生巔峯。
可以的話請給我一個三連支持一下我喲🧐🧐🧐【獲取資料】