LinkedList 双向循环链表和双向链表的区别

从JDK1.7开始,LinkedList 由双向循环链表改为双向链表

首先,简单介绍一下LinkedList:

LinkedList是List接口的双向链表实现。由于是链表结构,所以长度没有限制;而且添加/删除元素的时候,只需要改变指针的指向(把链表断开,插入/删除元素,再把链表连起来)即可,非常方便,而ArrayList却需要重整数组 (add/remove中间元素)。所以LinkedList适合用于添加/删除操作频繁的情况。

在JDK 1.7之前(此处使用JDK1.6来举例),LinkedList是通过headerEntry实现的一个循环链表的。先初始化一个空的Entry,用来做header,然后首尾相连,形成一个循环链表:

在LinkedList中提供了两个基本属性size、header。

private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;

 其中size表示的LinkedList的大小,header表示链表的表头,Entry为节点对象。

 1 private static class Entry<E> {
 2         E element;        //元素节点
 3         Entry<E> next;    //下一个元素
 4         Entry<E> previous;  //上一个元素
 5 
 6         Entry(E element, Entry<E> next, Entry<E> previous) {
 7             this.element = element;
 8             this.next = next;
 9             this.previous = previous;
10         }
11     }

        每次添加/删除元素都是默认在链尾操作。对应此处,就是在header前面操作,因为遍历是next方向的,所以在header前面操作,就相当于在链表尾操作。

如下面的插入操作addBefore以及图示,如果插入obj_3,只需要修改header.previous和obj_2.next指向obj_3即可。

 1 private Entry<E> addBefore(E e, Entry<E> entry) {
 2         //利用Entry构造函数构建一个新节点 newEntry,
 3         Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
 4         //修改newEntry的前后节点的引用,确保其链表的引用关系是正确的
 5         newEntry.previous.next = newEntry;
 6         newEntry.next.previous = newEntry;
 7         //容量+1
 8         size++;
 9         //修改次数+1
10         modCount++;
11         return newEntry;
12 }

 在addBefore方法中无非就是做了这件事:构建一个新节点newEntry,然后修改其前后的引用。

##########################################################################################################################################

  在JDK 1.7,1.6的headerEntry循环链表被替换成了first和last组成的非循环链表。

transient int size = 0;

    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;

在初始化的时候,不用去new一个Entry。

  /**
     * Constructs an empty list.
     */
    public LinkedList() {
    }

在插入/删除的时候,也是默认在链尾操作。把插入的obj当成newLast,挂在oldLast的后面。另外还要先判断first是否为空,如果为空则first = obj。

如下面的插入方法linkLast,在尾部操作,只需要把obj_3.next指向obj_4即可。

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

其中:

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

 

posted @ 2019-11-21 19:54  尐海爸爸  阅读(5393)  评论(0编辑  收藏  举报