LinkedList源码分析

1. LinkedList初始化

java 复制代码
public class LinkedListTest {
    public static void main(String[] args) {

        LinkedList<String> list = new LinkedList<String>();

        // 新增
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add(2, "e");

        // 删除
        list.remove(1);

        // 修改
        list.set(1, "d");

        // 查询
        list.get(0);
    }
}

在创建LinkedList时,可以看到无参构造方法里面没有任何代码,所以创建时,只是创建一个空的集合。

往上面翻可以看到这里有三个参数,分别是size,first和last。first和last分别是头结点和尾结点,说明LinkedList是一个双向链表。

查看结点类,可以看到里面有数据item,下一个节点next,上一个结点prev,也可以看出是一个双向的结点。

2. 新增元素

新增元素时,先进入add()方法,并将需要添加的元素作为参数传入,然后执行linkLast()方法。

首先先将last赋值给一个临时变量l,然后去创建一个新的节点,即new Node<>(l, e, null),然后将l,e,null传入进去。

下面就是创建新结点的过程,由于是创建LinkedList后第一次新增元素,因此上一个结点和下一个结点都为null,元素item则为新增的元素"a"。

创建好新结点newNode之后,如果是空链表,则直接将新节点赋值给first,如果之前存在元素,则将新节点赋值给l.next,即上一个元素指向新创建的元素。

这样成功的新增了一个元素。

3. 插入元素

还有一种在指定位置插入元素,即示例中的list.add(2, "e")。

凡是在某个位置进行操作时,第一步都是检验下标是否越界。这里下标没有越界,且index为2,不等于size,因此直接执行linkBefore(element, node(index))方法,从这个方法可以看出,就是element会去创建一个新的结点,然后新节点的的next指向下标为2的这个结点。

这里先进入node(index)这个方法进行查看,这里会有一个if判断,因为LinkedList是一个双向链表,不管是从头结点出发,还是从尾结点出发,都可以找到链表中的所有元素,但是链表在查找元素时,需要一个个的寻找,因此这里为了节省查找时间,先根据下标判断当前元素和size / 2的大小关系,如果index < size / 2, 则说明靠近头结点,从头结点开始寻找更为快速;反之如果index > size / 2, 则说明靠近尾结点,从尾结点开始寻找更为快速。我这里size为4,然后是在2这个位置插入元素,计算之后2 > 4 / 2为false,因此从尾结点开始寻找。

这里进去之后就和新增是差不多的操作,先记录下succ节点的上一个结点,结合上面我们的分析,这里的succ其实是下标为2的结点。然后根据插入的元素创建一个新的结点newNode,可以看出新结点的上一个结点为succ节点的上一个结点,元素为e,下一个结点为succ。

接着将succ的上一个结点指向新结点,然后进行判断pred是否为null,这里不为null,则将pred的下一个结点指向新结点。文字看起来可能比较绕,下面画了一张图进行分析。


4. 修改元素

对指定下标的元素进行修改,即示例中的list.set(1, "d")。

进入到set()方法之后,检验下标如果没有越界,则进行下一步。

这里的node(index)方法,依然和上面在插入元素时的方法一样,因此直接跳过。

这里的修改操作和ArrayList中的修改是类似的,也是将当前位置的元素赋值给oldVal,然后再将需要修改的值element赋值给x的元素值。最后将oldVal返回。

5. 删除元素

对指定下标的元素进行删除,即示例中的list.remove(1)。

进入到remove()方法之后,如果下标没有越界,则进行下一步unlink(node(index))操作,由于node(index)方法已经介绍过了,因此直接看

unlink()方法。

进入到unlink()方法之后,可以看到这里先获取到要删除节点x的元素,下一个节点和上一个结点。然后进行两个判断,

  1. 首先判断上一个结点是不是为null:
    a. 如果当前结点的上一个结点为null,说明这是第一个结点,直接将下一个节点作为头结点;
    b.如果当前结点的上一个结点不为null,则说明之前还有结点,那么就是将上一个结点的next指向下一个元素,并将当前元素的上一个结点赋值为null。
  2. 其次判断下一个结点是不是为null:
    a. 如果当前结点的下一个结点为null,说明这是最后一个结点,直接将上一个结点作为尾结点;
    b. 如果当前结点的下一个结点不为null,则说明之后还有结点,那么就是将下一个结点的prev指向上一个元素,并将当前元素的下一个结点赋值为null。

这样就完成了删除操作。

相关推荐
云雀衔光9 分钟前
MCP + 应用生成:让 AI 直接产出可交互的应用
java·人工智能·测试工具·microsoft·交互·ai编程
Nuanyt23 分钟前
JVM常见核心知识梳理02 类加载 字节码技术 双亲委派 Java内存模型 JMM 并发底层 volatile synchronized 常见排障与调优工具
java·开发语言·jvm
Shan120531 分钟前
经典算法题示例与详解:飞地的数量(二)
java·数据结构·算法
一嘴一个橘子32 分钟前
java - redis 缓存雪崩
java
小溪学编程1 小时前
Java ListIterator 接口详解:双向遍历与列表修改的利器
java·开发语言·windows
得物技术1 小时前
企业级 MultiAgent 落地:Plan 模式与主子 Agent 协作|得物技术
java·程序员·架构
程序员清风1 小时前
为什么不推荐走Agent开发?
java·后端·面试
我命由我123451 小时前
Compose Codelab 学习 - Jetpack Compose 中的状态
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
土司大王1 小时前
LeetCode hot100——全排列
java·算法·leetcode
sbjdhjd1 小时前
在 8 字符、字母数字过滤与 PHP 版本差异下复盘临时文件命令执行链 | (8字符绕过)
android·java·开发语言·安全·web安全·数据挖掘·php