這次牛逼了,面試字節被問LinkedList原理了!手足無措啊 概述 數據結構 主要方法解析 刪除元素

概述

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,快速升職加薪,走上人生巔峯。

可以的話請給我一個三連支持一下我喲🧐🧐🧐【獲取資料】

[圖片上傳失敗...(image-bf25be-1623855202037)]

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