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。

这样就完成了删除操作。

相关推荐
考虑考虑18 小时前
Jpa使用union all
java·spring boot·后端
用户37215742613518 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊19 小时前
Java学习第22天 - 云原生与容器化
java
渣哥21 小时前
原来 Java 里线程安全集合有这么多种
java
间彧21 小时前
Spring Boot集成Spring Security完整指南
java
间彧1 天前
Spring Secutiy基本原理及工作流程
java
Java水解1 天前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆1 天前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学1 天前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole1 天前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端