LinkedList源码解读

LinkedList源码解读

本文基于jdk16

1、LinkedList底层结构

(1)LinkedList底层维护了一个双向链表

(2)LinkedList中维护了两个属性first和last分别指向首节点和尾节点

transient Node<E> first;
transient Node<E> last;

(3)每一个节点都是一个Node对象,Node对象中又维护了prev、next、item三个属性,prev代表前一个节点,next代表后一个节点,item代表了具体的值,通过这三个属性实现双向链表

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

(4)LinkedList元素的添加删除不是通过数组实现的,相对来说效率较高

(5)LinkedList在线程同步中是不安全的,因为其操作方法没有实现同步

2、源码解读

(1)初始化

在这里插入图片描述

通过上面的LinkedList linkedList = new LinkedList();调用无参构造创建LinkedList对象

public LinkedList() {
}
(2)添加元素

在这里插入图片描述

从上面linkedList.add(“张三”);进入以下代码

public boolean add(E e) {
    //具体的添加操作
    linkLast(e);
    return true;
}

从上面的linkLast(e);进入以下代码

第一次添加元素,首先将新创建的节点赋值给last,然后在进入到if中,将新添加进来的节点赋值给first,

//e为要添加的元素
void linkLast(E e) {
    //将linkedList的last赋值给l
    final Node<E> l = last;
    //创建新节点  prev为l即last 元素值为e next为null
    final Node<E> newNode = new Node<>(l, e, null);
    //将新创建的节点赋值给last
    last = newNode;
    //如果l即原来的last为空时,即LinkedList为空时进入if
    if (l == null)
        //LinkedList为空时first等于新创建的节点
        first = newNode;
    //当LinkedList不为空时
    else
        //原先的最后一个节点的下一个节点为新创建的节点
        l.next = newNode;
    //LinkedList的size加一
    size++;
    //被修改次数加一
    modCount++;
}

非第一次添加元素,first不变,然后在进入else中,将新加入的节点的值赋值给原先最后一个节点的next

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HL7Zs7bX-1626599805742)(C:\Users\14538\AppData\Roaming\Typora\typora-user-images\image-20210718163344694.png)]

(3)删除元素(头元素)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jT8xtmFT-1626599805745)(C:\Users\14538\AppData\Roaming\Typora\typora-user-images\image-20210718163523846.png)]

从上面的linkedList.remove()进入以下代码

public E remove() {
    //真正的操作方法
    return removeFirst();
}

从上面的removeFirst()进入以下代码

public E removeFirst() {
    //f为此时的头元素
    final Node<E> f = first;
    //如果f为null,则说明此时LinkedList中无元素,则抛出异常
    if (f == null)
        throw new NoSuchElementException();
    //移除头元素方法
    return unlinkFirst(f);
}

从上面的unlinkFirst(f)进入以下代码

private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    //element为头元素的值,因为移除元素需要返回被移除元素的值
    final E element = f.item;
    //next为头元素的下一个节点
    final Node<E> next = f.next;
    //将头元素的item、和next置为null
    f.item = null;
    f.next = null; // help GC
    //将原先头元素的下一个元素赋值给头元素
    first = next;
    //如果next为空,则说明原LinkedList中只有一个元素,则也要将last置为空
    if (next == null)
        last = null;
    //else代表了原LinkedList还存在元素
    else
        //此时的first = next 将next的前一个节点置为空
        next.prev = null;
    //linkedList长度减一
    size--;
    //被修改的次数加一
    modCount++;
    //返回被移除的元素的值
    return element;
}
(4)修改元素(修改某个位置上的元素)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zo8zODMf-1626599805748)(C:\Users\14538\AppData\Roaming\Typora\typora-user-images\image-20210718164656249.png)]

从上面的linkedList.set(1,“赵虎”);进入以下代码

public E set(int index, E element) {
    //检查索引是否存在越界
    checkElementIndex(index);
    //获取到该索引所在节点
    Node<E> x = node(index);
    //被修改的节点的值,用于返回
    E oldVal = x.item;
    //修改节点的值
    x.item = element;
    //返沪被修改的节点值
    return oldVal;
}

从上面的Node x = node(index);进入以下代码

//需要修改的值
Node<E> node(int index) {
    // assert isElementIndex(index);
	//如果index小于当前linkedList元素个数的一半进入if,从头开始找到该节点
    if (index < (size >> 1)) {
        //x为头节点
        Node<E> x = first;
        //这里可以这样理解,如index为1,则代表要找到头节点的后一个节点
        for (int i = 0; i < index; i++)
            x = x.next;
        //返回索引值对应的节点
        return x;
    //如果index大于或等于当前linkedList元素个数的一半进入else,从尾开始找到该节点
    } else {
        //x为尾节点
        Node<E> x = last;
        //从最后一个节点开始往前找,找到index索引值对应的节点
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        //返回索引值对应的节点
        return x;
    }
}
(5)获取元素值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o9gP4NHC-1626599805750)(C:\Users\14538\AppData\Roaming\Typora\typora-user-images\image-20210718170714601.png)]

public E get(int index) {
    //检查索引是否存在越界
    checkElementIndex(index);
    //此处的node(index)与上面修改元素获取节点操作相同 可查看上面的源码解释
    return node(index).item;
}

3、ArrayList与LinkedList比较

ArrayList和LinkedList的比较

底层结构增删的效率改查的效率
ArrayList可变数组Object[]较低(因为需要数组扩容)较高
LinkedList双向列表较高(通过链表进行追加)较低

在使用时该如何选择ArrayList和LinkedList:

1)如果需要进行改查的操作多,选择ArrayList;

2)如果需要进行增删的操作多,选择LinkedList;

3)一般来说,在程序中,我们使用更多的是查询,因此大部分情况下会选择ArrayList;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值