Java集合学习:LinkedList源码详解

news2024/11/19 1:34:51

前言

LinkedList在我们平时工作中使用频率非车高,底层是基于双向链表数据结构实现,下面从经常使用的几个方法来了解其原理。

正文

在这里插入图片描述

结构

我们先看下LinkedList的重要属性

	/**
		存储链表数量
	*/
    transient int size = 0;

    /**
		 存储链表的头节点
     */
    transient Node<E> first;

    /**
    	存储链表的尾节点
     */
    transient Node<E> last;
	/**
	* 节点
	*/
    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;
        }
    }

添加方法

方法:add(E e)

    public boolean add(E e) {
        linkLast(e);
        return true;
    }

方法:add(int index, E element)

    public void add(int index, E element) {
    	//校验下标范围是否在链表范围内
        checkPositionIndex(index);
		//插入位置等于链表容量大小,证明是需要插入到尾部。
		//因为下标是从0算起的,所以链表中size大小的位置肯定是为空的。
        if (index == size)
            linkLast(element);
        else
        	//找出下标对应链表中的节点,并在其前面进行插入
            linkBefore(element, node(index));
    }

这里会根据下标找出当前链表中的那个节点,然后在其前面位置进行插入,相当于顶替了其位置,将原来那个节点挤到它的后面去。
查找过程如下:

方法:linkLast(E e)

    void linkLast(E e) {
    	//保存一下尾节点
        final Node<E> l = last;
        //创建一个新的节点,并对其属性进行赋值。将e作为其数据值,
        //将其上节点指向l,将下节点指向null
        
        final Node<E> newNode = new Node<>(l, e, null);
        //将链表的尾部引用指向新添加的节点
        last = newNode;
        //如果头节点为空,证明新创的节点为该链表中的第一个
        if (l == null)
            first = newNode;
        else
        		//将新节点与之前的尾节点进行关联
            l.next = newNode;
        size++;
        modCount++;
    }

该方法采用尾插法,将数据放到尾部。

方法:linkBefore(E e, Node succ)

    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        //保存其上个节点引用的值
        final Node<E> pred = succ.prev;
        //新建节点,将其下个节点引用指向succ,也就是上面通过下标查找出来的那个节点
        final Node<E> newNode = new Node<>(pred, e, succ);
        //将succ上个节点指向新插入的节点,建立关联
        succ.prev = newNode;
        if (pred == null)
        	//进入这里,证明之前通过下标查找出来的值是个头节点
            first = newNode;
        else
        	//将上节点的引用指向新节点
            pred.next = newNode;
        size++;
        modCount++;
    }

方法:linkFirst(E e)

    private void linkFirst(E e) {
    	//保存第一个节点
        final Node<E> f = first;
        //创建新节点,将尾部指向当前链表的头节点,其上节点赋值为null
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        
        if (f == null)
        	//进入这里代表当前链表是空的,所以尾部节点赋值为新插入节点
            last = newNode;
        else
        	//将之前链表的头节点与新插入节点建立连接
            f.prev = newNode;
        //统计值
        size++;
        modCount++;
    }

查询方法

方法:get(int index)

    public E get(int index) {
    	//校验下标是否在链表当前容量大小范围内
        checkElementIndex(index);
        return node(index).item;
    }
    Node<E> node(int index) {
        // assert isElementIndex(index);
		//将二进制位右移动1位,相当于除2
		//看是位于左边还是右边
        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;
        }
    }

通过二切法可以提高查询效率。

方法:peek()

    public E peek() {
    	//获取头节点值
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }

方法:poll()

    public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

获取头节点值,并移除头节点

删除方法

方法:remove()

    public E remove() {
        return removeFirst();
    }
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(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;
    }

方法:remove(int index)

    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
    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;
        }
	//将值置为NULL
        x.item = null;
        size--;
        modCount++;
        return element;
    }

总结

链表不需要指定容量,只要内存够大,就可以一直存储下去。链表存储时,分配完内存空间后,只需要将引用进行关联就可以,比起ArrayList可能会造成空间移动,效率高得多。但是在存储时,我们看到它是采用遍历的方式,进行下标查询,随便使用了二分进行切割,但在数据量大的情况下自旋时间长,对CPU的消耗比较大。

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

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

相关文章

MTK平台使用Omnipeek分析空口协议讲解

讲解这个之前,我们先来了解下beacon/robe Request/Probe Response 三种帧 beacon帧 信标帧,由AP以一定的时间间隔周期性发出,以此来告诉外界自己无线网络的存在。 Beacon帧作为802.11中一个周期性的帧,Beacon周期调高,对应睡眠周期拉长,故节能(即越来休息100ms再起来…

Laravel框架04:视图与CSRF攻击

Laravel框架04&#xff1a;视图与CSRF攻击一、视图概述二、变量分配与展示三、模板中直接使用函数四、循环与分支语法标签五、模板继承、包含1. 继承2. 包含六、外部静态文件引入七、CSRF攻击概述八、从CSRF验证中排除例外路由一、视图概述 视图存放在 resources/views 目录下…

Verilog 学习第五节(串口接收部分)

小梅哥串口部分学习part2 串口通信接收原理串口通信接收程序设计与调试巧用位操作优化串口接收逻辑设计串口接收模块的项目应用案例串口通信接收原理 在采样的时候没有必要一直判断一个clk内全部都是高/低电平&#xff0c;如果采用直接对中间点进行判断的话&#xff0c;很有可能…

CISP注册信息安全专业人员证书

一、什么是“CISP”&#xff1f; 注册信息安全专业人员(Certified Information Security Professional&#xff0c;简称“CISP”)&#xff0c;是安全行业最为权威的安全资格认证&#xff0c;由中国信息安全测评中心统一授权组织&#xff0c;中国信息安全测评中心授权培训机构进…

学习笔记之Vue中的Ajax(四)

Vue中的Ajax&#xff08;四&#xff09;Vue中的ajax一、解决开发环境Ajax跨越问题二、github 用户搜索案例2.1 准备工作2.2 静态页面2.3 实现动态组件2.4 注意细节三、vue 项目中常用的 2 个 Ajax 库3.1 axios3.2 vue-resource四、slot插槽&#xff08;四&#xff09;Vue中的aj…

计算结构体大小

计算结构体大小 目录计算结构体大小一. 结构体内存对齐1. 简介2. 嵌套结构体二. offsetof三. 内存对齐的意义四. 修改默认对齐数一. 结构体内存对齐 以字节&#xff08;bety&#xff09;为单位 1. 简介 对于结构体成员在内存里的存储&#xff0c;存在结构体的对齐规则&#…

代码随想录算法训练营day44 | 动态规划之完全背包 518. 零钱兑换 II 377. 组合总和 Ⅳ

day44完全背包基础知识问题描述举个栗子518. 零钱兑换 II1.确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组377. 组合总和 Ⅳ1.确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例来推导dp数组完全背包基…

安全合规之CVE-2016-2183

文章目录概述分析解决补充信息概述 安全部门脆弱性扫描到如下的风险漏洞要求系统上线必须要修复完毕。 不过我仔细的看了安全部门返回的报告&#xff0c;它是针对Windows Server 2019远程桌面端口进行风险报告…这是刷存在感了吗&#xff1f;哎&#xff0c;没有办法先做调查确…

高压放大器在声波谐振电小天线收发测试系统中的应用

实验名称&#xff1a;高压放大器在声波谐振电小天线收发测试系统中的应用研究方向&#xff1a;信号传输测试目的&#xff1a;声波谐振电小天线颠覆了传统电小天线以电磁波谐振作为理论基础的天线发射和接收模式&#xff0c;它借助声波谐振实现电磁信号的辐射或接收。因为同频的…

Spring Batch 综合案例实战-项目准备

目录 案例需求 分析 项目准备 步骤1&#xff1a;新开spring-batch-example 步骤2&#xff1a;导入依赖 步骤3&#xff1a;配置文件 步骤4&#xff1a;建立employee表与employe_temp表 步骤5&#xff1a;建立基本代码体系-domain-mapper-service-controller-mapper.xml …

YMatrix + PLPython替代Spark实现车联网算法

PySpark算法开发实战 一、PySpark介绍 Spark是一种快速、通用、可扩展的大数据分析引擎&#xff0c;PySpark是Spark为Python开发者提供的API。在有非常多可视化和机器学习算法需求的应用场景&#xff0c;使用PySpark比Spark-Scala可以更好地和python中丰富的库配合使用。 使…

监听页面滚动,给页面中的节点添加动态过渡效果

效果示例图 示例代码 <template><div class"animation-wrap"><!-- header-start --><div class"animation-header">头部</div><!-- header-end --><div class"animation-subtitle animation-show">标…

工人搬砖-课后程序(JAVA基础案例教程-黑马程序员编著-第八章-课后作业)

【案例8-4】 工人搬砖 【案例介绍】 1.任务描述 在某个工地&#xff0c;需要把100块砖搬运到二楼&#xff0c;现在有工人张三和李四&#xff0c;张三每次搬运3块砖&#xff0c;每趟需要10分钟&#xff0c;李四每次搬运5块砖&#xff0c;每趟需要12分钟。本案例要求编写程序分…

收集分享一些AI工具第三期(网站篇)

感谢大家对于内容的喜欢&#xff0c;目前已经来到了AI工具分享的最后一期了&#xff0c;目前为止大部分好用的AI工具都已经介绍给大家了&#xff0c;希望大家可以喜欢。 image-to-sound-fx (https://huggingface.co/spaces/fffiloni/image-to-sound-fx) 图片转换为相对应的声音…

【unity3d】unity即时战略游戏开发2 rts engine

A 背景 经过寻找发现有unity3d的[rts engine]&#xff0c;ue4的[template 4]等rts引擎/模板。 没有搜到相关教程&#xff0c;倒是有几个老外的ue从零开发长篇教程。 rts engine有几个试玩视频&#xff0c;尝试找了一下。那就不用虚幻了。 距离[原坤争霸 genshin craft]近了…

【ChatGPT整活大赏】写论文后自动生成视频

ChatGPT国内又火了一把&#xff0c;功能很强大&#xff0c;接下来就带大家感受一下它的强大之处&#xff0c;通过ChatGPT写一篇论文并自动生成视频&#xff0c;增加内容的可读性。 话不多说&#xff0c;先上成果&#xff1a; …

MySQL管理表

在创建表时需要提前了解mysql里面的数据类型 常见的数据类型 创建表 创建表方式1&#xff1a; 格式&#xff1a; CREATE TABLE [IF NOT EXISTS] 表名( 字段1, 数据类型 [约束条件] [默认值], 字段2, 数据类型 [约束条件] [默认值], 字段3, 数据类型 [约束条件] [默认值], ………

以FGSM算法为例的对抗训练的实现(基于Pytorch)

1. 前言 深度学习虽然发展迅速,但是由于其线性的特性,受到了对抗样本的影响,很容易造成系统功能的失效。 以图像分类为例子&#xff0c;对抗样本很容易使得在测试集上精度很高的模型在对抗样本上的识别精度很低。 对抗样本指的是在合法数据上添加了特定的小的扰动&#xff0c;…

聚类算法(下):10个聚类算法的评价指标

上篇文章我们已经介绍了一些常见的聚类算法&#xff0c;下面我们将要介绍评估聚类算法的指标 1、Rand Index Rand Index&#xff08;兰德指数&#xff09;是一种衡量聚类算法性能的指标。它衡量的是聚类算法将数据点分配到聚类中的准确程度。兰德指数的范围从0到1,1的值表示两…

Python-GEE遥感云大数据分析、管理与可视化技术及多领域案例实践应用

随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近年来遥感技术突飞猛进。由此&#xff0c;遥感数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量也大幅增长&#xff0c;使其越来越具有大数据特征。对于相关研究而言&#xff0c;遥感大数据的出现为其提…