LinkedList 双向链表源码分析

目录

  • LinkedList介绍
  • Node内部类
  • LinkedList源码分析

LinkedList 介绍

在分析缓存淘汰算法LRU时,双向链表是其中一种实现方式,动手实现时才发现head和tail在没有其他结点时的处理很别扭,所以就研究下LinkedList的源码,找点思路;

首先看看关于LinkedList的简介

LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,能对它进行队列操作。
LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
LinkedList 是非同步的。


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类很好理解,是个泛型类,由三要素构成:

  • 当前结点的前继结点
  • 当前结点的后续结点
  • 当前结点对应的泛型值;

Node结点在创建实例对象时,就需要对其前后结点以及结点对应的值都初始化;


LinkedList 类源码

注意理解双向链表的核心设计思路,LinkedList的first和last都只是指向链表中的node结点实例对象的引用,first和last自身并没有被创建成单独的结点对象,理解这一点很关键;

实例变量域

// 当前链表的元素个数
transient int size = 0;

protected transient int modCount = 0;

// 指向链表的第一个结点
transient Node<E> first;

// 指向链表的最后一个结点
transient Node<E> last;

 

linkLast方法:向链表尾部添加结点

    /**
     * Links e as last element.
     */
    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++;
    }

// ------------------ 当向链表中首次添加结点时 上述方法可简化为:---------------------------
void linkLast(E e) {
        final Node<E> l = last = null;
        final Node<E> newNode = new Node<>(null, e, null);
        last = newNode;
        first = newNode;
        size++;
        modCount++;
}
// ------------------ 当之后再向链表中添加结点时 上述方法可简化为:-------------------------
void linkLast(E e) {
        final Node<E> newNode = new Node<>(last, e, null);
        final Node<E> l = last;
        last = newNode;
        l.next = newNode;
        size++;
        modCount++;
}

在链表为空时,first和last结点都是指向null;

当第一次向链表尾部添加结点时,

  • 创建一个新结点,prev和next都指向null;
  • 由于此时first和last都没有任何指向(都为null),所以讲first和last都指向该新结点;

当第二次(i > 1)向链表尾部添加结点时,

  • 创建一个新结点,prev指向last指向的旧的尾结点,next指向null;
  • last重新指向新尾结点;
  • last之前指向的尾结点,将其next指向新的尾结点;

linkFirst方法:向链表头部添加结点

    /**
     * Links e as first element.
     */
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

当第一次向链表头部添加结点时,

  • 创建一个新结点,prev和next都指向null;
  • 由于此时first和last都没有任何指向(都为null),所以讲first和last都指向该新结点;

当第二次(i > 1)向链表头部添加结点时,

  • 创建一个新结点,prev指向null,next指向first指向的旧的头结点;
  • first重新指向新的头结点;
  • 旧的头结点的prev指向新的头结点;

unlink方法:从链表中移除一个指定的非null结点

    /**
     * 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) {
            // 如果node是第一个结点,就将第二个结点作为first
            first = next;
        } else {
            // 如果node不是第一个结点,就将前驱结点next指向后继结点;
            // 并剪断x对前驱结点的引用,有利于GC回收
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            // 如果node是最后一个结点,就将倒数第二个结点作为最后一个结点
            last = prev;
        } else {
            // 如果node不是最后一个结点,就将后继结点前驱指向前驱结点;
            // 并简单x对后继结点的引用,有利于GC回收
            next.prev = prev;
            x.next = null;
        }
        // 截断x对item泛型内容的引用,有利于GC回收,至此x结点已经彻底剪断对所有资源的引用
        x.item = null;
        size--;
        modCount++;
        // 从链表中删除x结点后,返回对x的item内容的引用,以备他用
        return element;
    }

 

remove方法:根据指定的内容,删除对应的结点

    // remove根据指定的内容o,来移除相应的结点,remove是基于unlink方法实现移除功能;
    // remove支持移除结点item为null的情景
    public boolean remove(Object o) {
        if (o == null) {
            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;
    }

 


参考文献

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