成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

LinkedList 源碼分析,你想知道的都在這里

開發 后端
LinkedList是一種可以在任何位置進行高效地插入和移除操作的有序序列,它是基于雙向鏈表實現的,是線程不安全的,允許元素為null的雙向鏈表。

概述

LinkedList是一種可以在任何位置進行高效地插入和移除操作的有序序列,它是基于雙向鏈表實現的,是線程不安全的,允許元素為null的雙向鏈表。

源碼分析

變量

/**
 * 集合元素數量
 **/
transient int size = 0;

/**
 * 指向第一個節點的指針
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */
transient Node<E> first;

/**
 * 指向最后一個節點的指針
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node<E> last;

構造方法

/**
 * 無參構造方法
 */
public LinkedList() {
}

/**
 * 將集合c所有元素插入鏈表中
 */
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

Node節點

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

因為一個Node既有prev也有next,所以證明它是一個雙向鏈表。

添加元素

addAll(Collection c)

將集合c添加到鏈表,如果不傳index,則默認是添加到尾部。如果調用addAll(int index, Collection c)方法,則添加到index后面。

/**
 * 將集合添加到鏈尾
 */
public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}

/** 
 * 
 */
public boolean addAll(int index, Collection<? extends E> c) {
    checkPositionIndex(index);

    // 拿到目標集合數組
    Object[] a = c.toArray();
    //新增元素的數量
    int numNew = a.length;
    //如果新增元素數量為0,則不增加,并返回false
    if (numNew == 0)
        return false;
    
    //定義index節點的前置節點,后置節點
    Node<E> pred, succ;
    // 判斷是否是鏈表尾部,如果是:在鏈表尾部追加數據
    //尾部的后置節點一定是null,前置節點是隊尾
    if (index == size) {
        succ = null;
        pred = last;
    } else {
        // 如果不在鏈表末端(而在中間部位)
        // 取出index節點,并作為后繼節點
        succ = node(index);
        // index節點的前節點 作為前驅節點
        pred = succ.prev;
    }

    // 鏈表批量增加,是靠for循環遍歷原數組,依次執行插入節點操作
    for (Object o : a) {
        @SuppressWarnings("unchecked") 
        // 類型轉換
        E e = (E) o;
        // 前置節點為pred,后置節點為null,當前節點值為e的節點newNode
        Node<E> newNode = new Node<>(pred, e, null);
        // 如果前置節點為空, 則newNode為頭節點,否則為pred的next節點
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        pred = newNode;
    }

    // 循環結束后,如果后置節點是null,說明此時是在隊尾追加的
    if (succ == null) {
        // 設置尾節點
        last = pred;
    } else {
    //否則是在隊中插入的節點 ,更新前置節點 后置節點
        pred.next = succ;
        succ.prev = pred;
    }

    // 修改數量size
    size += numNew;
    //修改modCount
    modCount++;
    return true;
}
/**
  * 取出index節點
  */ 
Node<E> node(int index) {
    // assert isElementIndex(index);

    // 如果index 小于 size/2,則從頭部開始找
    if (index < (size >> 1)) {
        // 把頭節點賦值給x
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            // x=x的下一個節點
            x = x.next;
        return x;
    } else {
        // 如果index 大與等于 size/2,則從后面開始找
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}
// 檢測index位置是否合法
private void checkPositionIndex(int index) {
    if (!isPositionIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 檢測index位置是否合法
private boolean isPositionIndex(int index) {
    return index >= 0 && index <= size;
}    

假設我們要在index=2處添加{1,2}到鏈表中,圖解如下:

第一步:拿到index=2的前驅節點 prev=ele1。

第二步:遍歷集合prev.next=newNode,并實時更新prev節點以便下一次。
遍歷:prev=newNode

第三步:將index=2的節點ele2接上:prev.next=ele2,ele2.prev=prev。

注意node(index)方法:尋找處于index的節點,有一個小優化,結點在前半段則從頭開始遍歷,在后半段則從尾開始遍歷,這樣就保證了只需要遍歷最多一半結點就可以找到指定索引的結點。

addFirst(E e)方法

將e元素添加到鏈表并設置其為頭節點(first)。

public void addFirst(E e) {
    linkFirst(e);
}

//將e鏈接成列表的第一個元素
private void linkFirst(E e) {

    final Node<E> f = first;
    // 前驅為空,值為e,后繼為f
    final Node<E> newNode = new Node<>(null, e, f);
    first = newNode;
    //若f為空,則表明列表中還沒有元素,last也應該指向newNode
    if (f == null)
        last = newNode;
    else
    //否則,前first的前驅指向newNode
        f.prev = newNode;
    size++;
    modCount++;
}
  • 拿到first節點命名為f。
  • 新創建一個節點newNode設置其next節點為f節點。
  • 將newNode賦值給first。
  • 若f為空,則表明列表中還沒有元素,last也應該指向newNode;否則,前first的前驅指向newNode。

addLast(E e)方法

將e元素添加到鏈表并設置其為尾節點(last)。

public void addLast(E e) {
    linkLast(e);
}
/**
 * 將e鏈接成列表的last元素
 */
void linkLast(E e) {
    final Node<E> l = last;
    // 前驅為前last,值為e,后繼為null
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    //最后一個節點為空,說明列表中無元素
    if (l == null)
        //first同樣指向此節點
        first = newNode;
    else
        //否則,前last的后繼指向當前節點
        l.next = newNode;
    size++;
    modCount++;
}

過程與linkFirst()方法類似,這里略過。

add(E e)方法

在尾部追加元素e。

public boolean add(E e) {
    linkLast(e);
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;
    // 前驅為前last,值為e,后繼為null
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    //最后一個節點為空,說明列表中無元素
    if (l == null)
        //first同樣指向此節點
        first = newNode;
    else
        //否則,前last的后繼指向當前節點
        l.next = newNode;
    size++;
    modCount++;
}

add(int index, E element)方法

在鏈表的index處添加元素element.

public void add(int index, E element) {
    checkPositionIndex(index);

    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}
/**
 * 在succ節點前增加元素e(succ不能為空)
 */
void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    // 拿到succ的前驅
    final Node<E> pred = succ.prev;
    // 新new節點:前驅為pred,值為e,后繼為succ
    final Node<E> newNode = new Node<>(pred, e, succ);
    // 將succ的前驅指向當前節點
    succ.prev = newNode;
    // pred為空,說明此時succ為首節點
    if (pred == null)
        // 指向當前節點
        first = newNode;
    else
        // 否則,將succ之前的前驅的后繼指向當前節點
        pred.next = newNode;
    size++;
    modCount++;
}

linkLast方法上文有講。
linkBefore(E e, Node<E> succ)方法步驟:

  • 拿到succ的前驅節點。
  • 新new節點:前驅為pred,值為e,后繼為succ : Node<>(pred, e, succ)。
  • 將succ的前驅指向當前節點。
  • pred為空,說明此時succ為首節點,first指向當前節點;否則,將succ之前的前驅的后繼指向當前節點。

5、獲取/查詢元素

get(int index)方法

根據索引獲取鏈表中的元素。

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

// 檢測index合法性
private void checkElementIndex(int index) {
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

// 根據index 獲取元素
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;
    }
}

node方法上文有詳細講解,這里不做介紹。

getFirst()方法

獲取頭節點。

public E getFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}

getLast()方法

獲取尾節點。

public E getLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return l.item;
}

6、刪除元素

remove(Object o)

根據Object對象刪除元素。

public boolean remove(Object o) {
    // 如果o是空
    if (o == null) {
        // 遍歷鏈表查找 item==null 并執行unlink(x)方法刪除
        for (Node<E> x = first; x != null; x = x.next) {
            if (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;
}

E unlink(Node<E> x) {
    // assert x != null;
    // 保存x的元素值
    final E element = x.item;
    //保存x的后繼
    final Node<E> next = x.next;
    //保存x的前驅
    final Node<E> prev = x.prev;

    //如果前驅為null,說明x為首節點,first指向x的后繼
    if (prev == null) {
        first = next;
    } else {
        //x的前驅的后繼指向x的后繼,即略過了x
        prev.next = next;
        // x.prev已無用處,置空引用
        x.prev = null;
    }

    // 后繼為null,說明x為尾節點
    if (next == null) {
        // last指向x的前驅
        last = prev;
    } else {
        // x的后繼的前驅指向x的前驅,即略過了x
        next.prev = prev;
        // x.next已無用處,置空引用
        x.next = null;
    }
    // 引用置空
    x.item = null;
    size--;
    modCount++;
    // 返回所刪除的節點的元素值
    return element;
}
  • 遍歷鏈表查找 item==null 并執行unlink(x)方法刪除。
  • 如果前驅為null,說明x為首節點,first指向x的后繼,x的前驅的后繼指向x的后繼,即略過了x。
  • 如果后繼為null,說明x為尾節點,last指向x的前驅;否則x的后繼的前驅指向x的前驅,即略過了x,置空x.next。
  • 引用置空:x.item = null。
  • 圖解。

remove(int index)方法

根據鏈表的索引刪除元素。

public E remove(int index) {
    checkElementIndex(index);
    //node(index)會返回index對應的元素
    return unlink(node(index));
}

E unlink(Node<E> x)  方法上文有詳解。

removeFirst()方法

刪除頭節點。

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指向前first的后繼,也就是列表中的2號位
    first = next;
    //如果此時2號位為空,那么列表中此時已無節點
    if (next == null)
        //last指向null
        last = null;
    else
        // 首節點無前驅 
        next.prev = null;
    size--;
    modCount++;
    return element;
}

原理與添加頭節點類似。

removeLast()方法

刪除尾節點(last)

public E removeLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return unlinkLast(l);
}

private 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指向前last的前驅,也就是列表中的倒數2號位
    last = prev;
    // 如果此時倒數2號位為空,那么列表中已無節點
    if (prev == null)
        // first指向null
        first = null;
    else
        // 尾節點無后繼
        prev.next = null;
    size--;
    modCount++;
    // 返回尾節點保存的元素值
    return element;
}

7、修改元素

修改元素比較簡單,先找到index對應節點,然后對值進行修改。

public E set(int index, E element) {
    checkElementIndex(index);
    // 獲取到需要修改元素的節點
    Node<E> x = node(index);
    // 保存之前的值
    E oldVal = x.item;
    // 執行修改
    x.item = element;
    // 返回舊值
    return oldVal;
}

8、與ArrayList的對比

優點:

  • 不需要擴容和預留空間,空間效率高。
  • 增刪效率高。

缺點:

  • 隨機訪問時間效率低。
  • 改查效率低。
責任編輯:姜華 來源: 今日頭條
相關推薦

2021-06-17 13:40:47

區塊鏈比特幣公有鏈

2019-11-04 09:07:48

DevOps互聯網IT

2019-04-24 08:31:43

分布式限流kafka

2020-03-18 18:20:19

區塊鏈數字貨幣比特幣

2018-11-28 10:39:01

5G網絡運營商

2017-08-15 15:35:21

大數據數據分析薪資秘密

2017-08-15 16:05:18

大數據數據分析薪資秘密

2021-07-01 09:00:00

安全數字化轉型滲透

2019-04-26 09:38:36

中臺平臺化轉型

2018-03-31 08:45:52

iPhone交通卡iOS 11.3

2022-11-08 15:55:34

鴻蒙開發套件

2017-01-11 08:37:07

Apache SparStreamingDataFrames

2021-07-02 14:09:36

開發技能代碼

2017-12-13 14:24:08

Google 開發者瀏覽器

2015-10-12 15:50:40

2018-05-10 08:50:31

AndroidGoogle 移動系統

2021-02-23 09:28:48

大數據數據分析

2019-10-29 15:28:40

Refs組件前端

2018-08-23 11:58:53

區塊鏈數字貨幣比特幣

2022-09-15 14:22:19

協作規范前后端
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日本成人福利视频 | 久久精品国产亚洲 | 日韩高清一区二区 | 久草免费在线视频 | 黄色永久免费 | 欧美成视频 | 一区中文字幕 | 国产精品久久久久久久久免费 | 国产福利在线 | 麻豆视频在线看 | 欧产日产国产精品视频 | 青青草中文字幕 | 欧美成人激情 | 91精品久久久久久久久久入口 | 天天爽综合网 | 成人不卡 | 欧美在线观看一区 | 亚洲日韩中文字幕一区 | 在线免费观看a级片 | 欧美精品一区二区三区在线四季 | 日本精品久久久久久久 | 91成人在线视频 | 中文字幕国产精品 | 精品成人av| 97久久精品午夜一区二区 | 中文成人无字幕乱码精品 | 欧美美女爱爱视频 | 成人av免费看 | 亚洲一区二区三区免费视频 | 亚洲一区二区在线 | 91视视频在线观看入口直接观看 | 99久久99 | 国产1区2区3区 | 久久伊 | av片在线观看网站 | 免费观看色 | 精品免费视频 | 久久久久国产精品 | 二区三区视频 | 日韩一区二区三区视频在线播放 | 欧美成人精品激情在线观看 |