【java基础】LinkedList源码解析

news2024/11/25 16:20:35

文章目录

  • 基本介绍
  • 构造器
  • 基础方法
    • linkFirst
    • linkLast
    • linkBefore
    • unlinkFirst
    • unlinkLast
    • unlink
    • node
    • indexOf
  • 方法分析
  • 总结

基本介绍

在java中,LinkedList就是使用双向链表存储元素,既然是链表,那么也就知道了该数据结构擅长添加和删除。对于需要频繁添加和删除的,我们应该使用LinkedList而表示ArrayList

在开始介绍LinkedList之前,我们先来看一下该类的属性

    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里面有一个内部类为Node,链表实际存储的就是Node

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

构造器

在LinkedList中,构造器就只有2个

在这里插入图片描述

一个为无参构造器,一个传入一个集合,根据集合初始化,addAll方法后面说明

对于链表的操作都很简单,这里就简单看一下一些主要方法的源码,其他大家自行查看

基础方法

LinkedList基本所有的方法都用到了下面的几个基础方法,下面就来了解一下吧

linkFirst

void linkFirst(E e) 这个方法会将元素添加到链表作为表头

    /**
     * Links e as first element.
     */
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

上面代码就不用解释了吧,很简单,就是根据传入的元素创建Node,然后将其作为链表头

linkLast

void linkLast(E e) 就是将元素添加到链表尾部,作为新的表尾

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

该方法和linkFirst基本一样,不解释了。

linkBefore

void linkBefore(E e, Node succ),将元素e插入到非空节点succ之前

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

这个方法也容易理解,就是根据传入元素创建Node,然后修改对应Node的执行即可

unlinkFirst

E unlinkFirst(Node f) 这个方法会删除掉表头

    /**
     * Unlinks non-null first node f.
     */
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        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;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

该方法就是改变fisrt指针的指向即可

unlinkLast

unlinkLast(Node l) 这个方法就会删除掉链表尾部的那个元素

    /**
     * Unlinks non-null last node l.
     */
    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        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;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

和unlinkFirst大同小异

unlink

E unlink(Node x) 会删除指定的节点

    /**
     * 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) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

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

可以发现,该方法其实就是改变要删除节点的前一个节点和后一个节点的指针指向。

node

Node node(int index) 返回指定索引位置的Node

    /**
     * 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超过size的一半,那么就会从后面开始查找

indexOf

int indexOf(Object o) 返回某个元素在链表中第一次出现的位置

    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

这个方法就是遍历链表,找到就返回索引

方法分析

上面说的那些方法,全部都是其他方法的基础,其他方法基本都是通过上面的那几个方法实现的。对于其他api,请参考下面内容

第一个是关于双端队列的方法,如果学过数据结构,那么这些方法也就知道是什么意思了

在这里插入图片描述

主要还有List里面的方法

在这里插入图片描述

LinkedList实现了所有的方法,基础就是上面介绍的方法,其实就是通过移动链表的指向完成的

总结

LinkedList比较简单,就是关于链表的各种操作,学过数据结构那么就是小菜一碟了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/403347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

线程等待/休眠/状态及 Runnable 和 Callable 的简单使用及原理

关于线程和进程的基本概念☛操作系统中线程和进程的概念理解 这篇文章已经有了很详细的解释, 接下来主要来讲讲线程等待与线程休眠 / 线程的几种状态 / Runnable 和 Callable 与 Thread 的概念和区别及 Executor 框架是什么样的. 关于线程1 线程等待与线程休眠2 线程一共有哪些…

[洛谷-P3698][CQOI2017]小Q的棋盘

一、问题 题目描述 小 Q 正在设计一种棋类游戏。 在小 Q 设计的游戏中&#xff0c;棋子可以放在棋盘上的格点中。某些格点之间有连线&#xff0c;棋子只能在有连线的格点之间移动。整个棋盘上共有 VVV 个格点&#xff0c;编号为 0,1,2,⋯,V−10,1,2,\cdots, V- 10,1,2,⋯,V−…

【C++知识点】C++11 常用新特性总结

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;C/C知识点 &#x1f4e3;专栏定位&#xff1a;整理一下 C 相关的知识点&#xff0c;供大家学习参考~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;…

EasyRcovery16免费的电脑照片数据恢复软件

电脑作为一种重要的数据储存设备&#xff0c;其中保存着大量的文档&#xff0c;邮件&#xff0c;视频&#xff0c;音频和照片。那么&#xff0c;如果电脑照片被删除了怎么办&#xff1f;今天小编给大家介绍&#xff0c;误删除的照片从哪里可以找回来&#xff0c;误删除的照片如…

win10打印机拒绝访问解决方法

一直以来,在安装使用共享打印机打印一些文件的时候&#xff0c;会遇到错误提示&#xff1a;“无法访问.你可能没有权限使用网络资源。请与这台服务器的管理员联系”的问题&#xff0c;那为什么共享打印机拒绝访问呢&#xff1f;别着急&#xff0c;下面为大家带来相关的解决方法…

mysql时区问题

设置mysql容器时间与服务器时间一致 问题背景&#xff1a; 今天测试发现一个问题&#xff0c;时间不一致&#xff0c;当工单入库时&#xff0c;其创建时间和更新时间应该是一样的&#xff0c;即使不一样最多只会错几秒的时间&#xff1b;实际上两个时间相差的大概8小时&#…

青少年学AI,Amazon DeepRacer有何魔力?

导读&#xff1a;北京名校中学生可以根据兴趣开发AI模型甚至发表论文&#xff0c;偏远地区的校长还在犹豫“人工智能教育&#xff0c;中考会考吗&#xff1f;高考会加分吗&#xff1f;”教育鸿沟由来已久&#xff0c;绝非仅靠某些企业或教育机构可以扭转&#xff0c;但我们至少…

maven仓库的配置

下载 官网下载&#xff1a;https://maven.apache.org/download.cgi 2. 配置maven环境 右键电脑 ->属性 -> 高级系统设置 -> 环境变量 -> 系统变量-新建 变量名&#xff1a;MAVEN_HOME 变量值为maven的文件安装地址 编辑Path系统变量 新建&#xff1a;%MAVE…

132.《render-props, Hoc,自定义hooks 详解》

文章目录render-props 模式props 方式children 方式(推荐)Hoc&#xff08;高阶组件&#xff09;使用步骤示例props 丢失解决方案自定义 hook1.只执行一次2.防抖hook高阶组件与自定义hook有什么区别相同点不同点React 中代码逻辑复用有三种方式&#xff0c;render-props, Hoc&am…

Altium Designer(AD)软件使用记录05-PCB叠层设计

目录Altium Designer(AD)软件使用记录05-PCB叠层设计一、正片层和负片层的介绍1、正片层(Signal)2、负片层(Plane)3、内电层的分割实现二、正片层和负片层的内缩设计1、负片设置内缩20H原则2、正片铺铜设置内缩1、设置规则2、重新铺铜三、AD的层叠设计四、叠层设计需要注意的问…

计算机组成原理_总线标准

计算机组成原理总目录总线标准 总线标准是系统与各模块、模块与模块之间的一个互连的标准&#xff0c;就像我们用汉语来相互交流一样。 1. 系统总线 ISA总线的扩展插槽&#xff0c;其颜色一般为黑色&#xff0c;比PCI接口插槽要长些&#xff0c;位于主板的最下端。 可插接显卡&…

Java中的深克隆与浅克隆

浅克隆&#xff1a; 实现Cloneable接口即可实现&#xff0c;浅克隆只对象内部的基础数据类型&#xff08;包括包装类&#xff09;被克隆&#xff0c;引用数据类型&#xff08;负责对象&#xff09;会被使用引用的方式传递。 简单来说&#xff0c;就是浅克隆属性如果是复杂对象…

java多线程(二五)ReentrantReadWriteLock读写锁详解(1)

一、读写锁简介 现实中有这样一种场景&#xff1a;对共享资源有读和写的操作&#xff0c;且写操作没有读操作那么频繁。在没有写操作的时候&#xff0c;多个线程同时读一个资源没有任何问题&#xff0c;所以应该允许多个线程同时读取共享资源&#xff1b;但是如果一个线程想去…

有关3dmax对齐技巧的那些事

建模操作中&#xff0c;对齐是非常常用的一个功能&#xff0c;用好这个对齐功能能够事半功倍&#xff0c;好处我不说了&#xff0c;下面我们这篇博文就来说说3dmax对齐技巧的相关的内容。 文章目录一、点对齐1、样条线中的点对齐2、多边形中的点对齐二、线对齐三、面对齐四、物…

DJI ROS dji_sdk 源码分析|整体框架

DJI ROS dji_sdk 源码分析|整体框架launch文件CMakeLists.txtcpp文件main.cppOSDK 是一个用于开发无人机应用程序的开发工具包&#xff0c;基于OSDK 开发的应用程序能够运行在机载计算机上&#xff08;如Manifold 2&#xff09;&#xff0c;开发者通过调用OSDK 中指定的接口能够…

计算机网络考研-第一章学

计算机网学习总结第一章计算机系统概述1.1.1 导学1.1.2 操作系统的特征1.2 操作系统的发展与分类1.3 操作系统的运行环境1.3.1 操作系统的运行机制1.3.2 中断和异常1.3.3系统调用&#xff1a;1.3.4 操作系统的体系结构第一章总结第一章计算机系统概述 1.1.1 导学 1.1.2 操作系…

Nginx 配置实例-反向代理案例一

实现效果&#xff1a;使用nginx反向代理&#xff0c;访问 www.suke.com 直接跳转到本机地址127.0.0.1:8080 一、准备工作 Centos7 安装 Nginxhttps://liush.blog.csdn.net/article/details/125027693 1. 启动一个 tomcat Centos7安装JDK1.8https://liush.blog.csdn.net/arti…

简单粗暴的分布式定时任务解决方案

分布式定时任务1.为什么需要定时任务&#xff1f;2.数据库实现分布式定时任务3.基于redis实现1.为什么需要定时任务&#xff1f; 因为有时候我们需要定时的执行一些操作&#xff0c;比如业务中产生的一些临时文件&#xff0c;临时文件不能立即删除&#xff0c;因为不清楚用户是…

基于FPGA实现正弦插值算法

1、正弦插值的算法分析 1.1 信号在时域与频域的映射关系 在进行正弦算法分析之前&#xff0c;我们回顾一下《数字信号处理》课程中&#xff0c;对于信号在时域与频域之间的映射关系&#xff0c;如下图。 对于上图中的原始信号x(t)&#xff0c;使用ADC对信号进行采样&#xff0…

【操作系统】进程句柄

进程句柄句柄是什么为什么需要句柄作用句柄是什么 先给结论&#xff0c;句柄&#xff08;handle&#xff09;实际上是一个指向指针的指针。 它指向进程所要访问的进程对象的地址&#xff0c;是用来找到目标进程的索引&#xff0c;当我们想要访问对象进程时&#xff0c;就要利…