LinkedList底层原理(源码剖析)

Java中的LinkedList的底层是由一个双向链表维护的,且包含头指针和尾指针。首先LinkedList是一个类,要了解一个类就需了解类的组成部分(成员变量、成员方法、类变量、构造方法及它们的访问权限如何,继承了什么类、实现了哪些接口)。下面对LinkedList主要的组成部分进行源码剖析:

1、LinkedList的家庭背景

在这里插入图片描述
从源码中可看到LinkedList继承了AbstractSequentialList,实现了List、Deque、Cloneable、Serializable接口。

2、LinkedList的节点Node

LinkedList类中有一个私有静态内部类Node,这就是双向链表的节点,该节点包含用于存数据的item、指向前一节点的指针prev和指向后一节点的指针next,源码如下:
在这里插入图片描述

3、LinkedList的成员变量

LinkedList有三个成员变量,且它们都被transient修饰,transient修饰符的作用就是在类被序列化时被transient修饰的不会被序列化。三个成员变量依次为size(记录链表节点的个数)、first(指向链表的第一个节点)、last(指向链表的最后一个节点)。源码如下:
在这里插入图片描述

4、LinkedList主要成员方法

一、插入
(1)、linkFirst(插入到头结点之前)

/**
     * Links e as first element.
     * 将e作为链表的第一个节点
     */
    private void linkFirst(E e) {
    	//先取到该链表的第一个节点,后面会用到
        final Node<E> f = first;
        //新建一个节点,即要插入的节点
        final Node<E> newNode = new Node<>(null, e, f);
        //让first指向新的节点
        first = newNode;
        //是否需更新尾指针
        if (f == null)//说明该链表是空的,更新尾指针
            last = newNode;//新添加的节点也是最后一个节点
        else//不为空,把原先的first即f的prev指向新节点就好
            f.prev = newNode; 
        size++;
        modCount++;
    }

(2)、linkLast(插入到尾节点之后)

 /**
     * Links e as last element.
     * 将e作为最后一个节点
     */
    void linkLast(E e) {
   	 	//先取到该链表的最后一个节点,后面会用到
        final Node<E> l = last;
        //新建一个节点,即要插入的节点
        final Node<E> newNode = new Node<>(l, e, null);
        //更新last
        last = newNode;
        //是否需要更新first
        if (l == null)//说明该链表是空的,更新first
            first = newNode;
        else//不为空,把原先最后一个节点的next指向新节点就好
            l.next = newNode;
        size++;
        modCount++;
    }

(3)、linkBefore(把节点插入到中间)

/**
     * Inserts element e before non-null Node succ.
     * 将e插入到succ之前,succ不为null,也就是说succ就是要插入的位置
     */
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;断言该succ不为null
        //取到succ的前一个节点
        final Node<E> pred = succ.prev;
        //新建一个节点,即要插入的节点
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)//说明succ是头节点
            first = newNode;//更新头节点
        else//让原先succ的前一个节点的下一个节点指向新节点
            pred.next = newNode;
        size++;
        modCount++;
    }

二、删除
(1)、unlinkFirst(删除头节点)

 /**
     * Unlinks non-null first node f.
     */
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        //先取到头节点的item用于返回值
        final E element = f.item;
        //取得头节点的下一个节点
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        //更新头节点
        first = next;
        if (next == null)//说明该链表只有一个节点就是头节点,
        //现在又把它删除了,所以链表为空,last也要为null
            last = null;
        else//next就作为了新的头节点
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

(2)、unlinkLast(删除尾节点)

/**
     * Unlinks non-null last node l.
     */
    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        //先取到尾节点的item用于返回值
        final E element = l.item;
        //取得尾节点的上一个节点
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        //更新尾节点
        last = prev;
        if (prev == null)//说明该链表只有一个节点就是尾节点,
        //现在又把它删除了,所以链表为空,first也要为null
            first = null;
        else//将prev的next置为null就行,prev就成了新的尾节点
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

(3)、unlink(删除中间节点)

 /**
     * 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) {//说明节点x是头节点
            first = next;//将first指向x的下一个节点即可
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {//说明节点x是尾节点
            last = prev;//将last指向x的上一个节点即可
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

LinkedList还有许多public的方法供我们调用,这些方法的内部实现基于上面这六个private或default方法,如add方法,源码如下:
在这里插入图片描述
调用了linkLast方法,将e添加到最后。
在这里插入图片描述
在这里插入图片描述
这里就不一一举例了,有想了解更多LinkedList方法的同学可以自己去看看源码,只要掌握以上六个方法的原理,其他方法迎刃而解。

总结: LinkedList是一个带头尾指针的双向链表结构,优势就在于它的插入和删除操作快捷方便。LinkedList实现了Deque接口,而Deque又继承了queue接口,故LinkedList也是queue(队列)的实现类,可实现先进先出的队列功能,当也可实现后进先出的栈功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

b17a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值