Java LinkedList 相关知识及其底层数据结构 源码

目录

LinkedList的数据结构从JDK1.7开始采用双向链表结构,在JDK1.6之前使用的数据结构是双向循环链表

add(E e)末尾添加元素

add(int index,E e):向指定位置插入新元素

结论:


LinkedList的数据结构从JDK1.7开始采用双向链表结构,在JDK1.6之前使用的数据结构是双向循环链表

LinkedList从1.7开始,底层采用的是双向链表数据结构。

双向链表由多个节点Node<E>构成,每个节点由三部分构成,分别是前驱节点(prev)、元素值(item)、后继节点(next),节点之间是游离状态存在的。

下面咱们来看源码:

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<E>类是LinkedList<E>的内部类,private修饰只服务于LinkedList。其中有成员变量E item元素值,前驱节点Node<E> prev,后继节点Node<E> next。其中的全参构造,参数依次是前驱节点、元素值、后继节点     这样就构成了一个节点。

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;

这是LinkedList<E>类的三个成员变量,首先呢第一个size是你LinkedList集合的长度是多少初始值为0,第二个first变量的意思是指向第一个节点(头节点)的指针,第三个last的意思是指向最后一个节点(尾节点)的指针。默认null。

add(E e)末尾添加元素

LinkedList增加元素的内部实现 add(E e)

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




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

add(E e)其中内部调用linkLast(E e)方法  在最后尾节点链元素  来看看源码是怎么玩的吧:

首先我们假设集合中是没有元素的,即LinkedList集合的三个成员变量均为初始值:size=0,Node类型的first和last为null。然后方法中定义局部变量Node<E> l = last;l引用指向咱们的尾节点,此时为null。接着new了一个Node创建一个节点,上面有介绍这个类,它的全参构造参数依次是前驱节点、元素值、后继节点。Node<E> newNode 新节点在末尾添加,所以它的前驱节点指向l也就是last尾节点,元素值为e,后继节点为null(因为把它添加在最后一个)。下行代码last = newNode;就是将LinkedList集合的成员变量last(尾节点)指向这个要添加在最后的元素newNode。接着,判断l是否为null,我们目前l为null,进入first也指向这个新添加的元素。(因为我们目前做的是集合中没有节点,添加一个节点,所以first和last都指向这个新添加的元素)。然后size结合长度加一,modCount++这个集合的操作次数加一。

下面来上一种流程图:

我们可知道,目前LinkedList集合刚创建出来没有节点,然后我们调用其add(E e)方法(内部将元素添加在链表最后)添加一个元素后,其中的成员变量指向:first头节点与last尾节点都指向这个新添加的节点,由于只有一个节点所以这个节点的前驱节点与后继节点都为null。

做得有点丑,哈哈!希望各位大佬看见了给点意见吧!

add(int index,E e):向指定位置插入新元素

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

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }


    
    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }



    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }

首先调用checkPositionIndex(index)这个方法,内部再调用isPositionIndex(index)。说白了就是先判断传进去的index是否越界了,这个指定位置应当是   index >= 0 && index <= size;

然后判断index是否等于size,如等于就是在链表最后添加,所以调用linkLast(element);同上面add(E e)末尾添加元素一个道理。 

如果index不等于size,便调用linkBefore(element, node(index));传参有个node(index)方法:

就是查找元素,链表查找元素:遵循对半查找,即查找的节点下标小于长度/2,从头节点开始挨个查找,若查找的节点下标大于长度/2,从尾结点开始逆向挨个查找!看源码↓↓↓

其中x.next表示的是后继节点的指向,x.prev表示的是前驱节点的指向。

    /**
     * Returns the (non-null) Node at the specified element 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;
        }
    }

 将index对应的节点求出传入linkBefore(E e,Node<E> succ)方法,第一个参数是你要插入的元素,第二个参数是你要插入的那个位置的原始元素,也就是上面找到的index对应的。

    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

如:假设集合中有4个元素,现在向下标为2的位置插入元素add(2,e),经过判断执行linkBefore(e, node(2))

其过程就是在修改链的指向。集合源码,还是很好理解的哦!删除元素目前就没有写了,道理都差不多。

结论:

双向链表优缺点:

  • 和数组相对而言,数据量大时,链表结构增删元素性能更高,查找元素性能偏低
  • 细节:若链表中查找的头结构或尾结点,查询性能不低  

查找元素:遵循对半查找,若查找的节点下标小于size/2,从头节点开始挨个查找,若查找的节点下标大于size/2,从尾结点开始逆向挨个查找。

ArrayList和LinkedList的区别:

ArrayList底层采用的是数组结构,LinkedList底层采用的是双向链表结构。
当数据量大时,ArrayList查询性能更高,增删元素性能较低。
LinkedList增删元素性能更高,查询元素性能较低。
​​​​​​​

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值