Java数据结构(5)-LinkedList

ArrayList和LinkedList的数据结构 本文将从实现的角度来描述ArrayList和LinkedList的数据结构及各自的应用场景 ArrayList 从名称就可以猜到ArrayList底层使用数组来实现List容器。数组是静态容器,即它不支持扩容或者删除数据,ArrayList在数组的基础上增加了动态扩容和删除,所以ArrayList也可以称之为动态数组。 ArrayList有两个重要的变量: ... 阅读详情

上一篇讲过,ArrayList在指定位置增加和删除性能较低。而有一种公认的观点:在经常添加和删除的场景下,LinkedList性能比ArrayList更高,这种说法是否可靠?本篇就来探讨一下。

Node结构

LinkedList集合中的每一个元素都是一个结点,将多个结点链接到一起,就是链表结构。以下是结点Node的源码,它是LinkedList的内部类。这段内容很简单,结点有三个引用变量和一个基本的构造方法。

    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;
        }
    }
  • E item:E是泛型,item就是list.add()方法的参数,即集合中的每个元素,都保存在其Node结点的item参数中。
  • Node<E> next:它指向当前结点的下一个结点。
  • Node<E> prev:它指向当前结点的上一个结点。


LinkedList结构 

在eclipse中调试以下代码

		list = new LinkedList();
		System.out.println("断点1");
		list.add(1);
		list.add(2);
		list.add(3);
		System.out.println("断点2");
  • 断点 1,创建一个空的LinkedList,下图可以看到其四个参数,modCount和size在ArrayList中也有,这两个我们不关心。重点关注first和last,用来存储链表的第一个节点和最后一个节点。那么,如何找到中间的节点?


  • 断点 2,添加了三个元素,此时first结点的next参数会指向第二个结点,即标识 373。而373的next参数又会指向第三个结点,即标识371。也就是说,链表结构可以从第一个结点找到第二个,再从第二个结点找到第三个,但是从第一个结点无法找到第三个,不能跳着找。很显然,这样一个一个的查找方式比ArrayList的数组查找方式要慢的多 。

添加的性能

上一篇测过,ArrayList添加5亿数据量仅耗时1080毫秒,现在就来看一下LinkedList能否打破ArrayList的纪录。

		int size=50000000;
		List list = new LinkedList();		
		long t1 = System.currentTimeMillis();
		for(int i=0;i<size;i++){
			list.add(1);
		}
		long t2 = System.currentTimeMillis();
		System.out.println(t2-t1);
  • 耗时:28531毫秒

注意:数据量只有5千万,少了十倍,性能反而慢了20多倍。这是为何?

内存分配的开销

上一篇讲过,list.add(1)并没有创建对象。而LinkedList集合中存储的是Node,通过Node的item来存储添加的1,因此LinkedList创建了5千万个Node对象。创建对象需要占用内存,当程序发现内存不够用就去分配内存。等分配的内存用完了又不够了,就又去分配,反复分配内存的开销才是真正的性能杀手。要想解决这个问题,可在eclipse中打开运行配置,设置jvm参数:-Xms7g,初始分配堆内存7g,一次性分配足够大的空间,就不用反复分配了。以后章节测试都会设置这个参数,不再解释。如下图


注意:vm参数7g是根据数据量来设定的,同时还要调大eclipse的内存。如果是5亿数据还得设的更大,我个人电脑内存有限,所以只测了5千万数据量。如果数据量不大,也可以设置一个较小的vm参数如100m。再次运行

  • 耗时:668毫秒

数据量比ArrayList少10倍,耗时却差不太多,性能明显低于ArrayList。原因其实上面已经说了,LinkedList创建了5千万个对象,而且还要维护结点之间的next和prev参数,而ArrayList不需要。下图是分别执行5千万数据量时,任务管理器中的内存走势图,可以明显看出LinkedList额外创建对象的内存开销。在java程序中,内存运行状况对时间性能的影响,往往比分析时间复杂度还更加重要。


从头部添加

上一篇说过,ArrayList从位置0添加数据,性能会很低。LinkedList表现又会如何?由于性能低下,现在把size改小到10万,分别测试它们

		int size=100000;
		List list = new ArrayList(size);	//测试ArrayList
		//List list = new LinkedList();		//测试LinkedList
		long t1 = System.currentTimeMillis();
		for(int i=0;i<size;i++){
			list.add(0,1);
		}
		long t2 = System.currentTimeMillis();
		System.out.println(t2-t1);
  • ArrayList耗时:765毫秒
  • LinkedList耗时:8毫秒

LinkedList性能高出10倍。如下图,LinkedList在结点1和结点2之间添加一个结点,只要修改它们的next和prev参数,分别指向新结点即可,而ArrayList却要移动n个元素。LinkedList终于找到了存在的价值

从中间添加

我们再换一个位置添加,比如说中间,看是什么效果。将list.add(0,1);改成list.add(i/2,1)

		int size=100000;
		List list = new ArrayList(size);	//测试ArrayList
		//List list = new LinkedList();		//测试LinkedList
		long t1 = System.currentTimeMillis();
		for(int i=0;i<size;i++){
			list.add(i/2,1);
		}
		long t2 = System.currentTimeMillis();
		System.out.println(t2-t1);
  • ArrayList耗时:343毫秒
  • LinkedList耗时:4971毫秒
ArrayList又比LinkedList快了10倍,为何?

查询的性能

  • LinkedList在添加时,首先要查找到添加的位置。如果位置在前半区,就从first结点开始,通过next一个个往后找。如果位置在后半区,就从last结点开始,通过prev一个个往前找。也就是说,离first或last最近的结点最容易找到,所以从头部和尾部添加性能最高。而离first和last最远的中间节点,就是LinkedList性能最低的位置。
  • ArrayList从头部添加的时候,需要移动的元素是最多的,也就是性能最低的。而从中间添加的时候,需要移动的元素处于平均数,所以反而性能还提高了。ArrayList添加时也要查找位置,但由于其使用数组,查询任意位置都是瞬间完成的,不存在性能问题。

删除的性能

删除结点时,会删除其前后链路,然后让其前后结点互相建立链路。删除和添加的原理是一样的,两头快,中间慢。


特性

  • 双端链表:记录了头结点和尾结点,处理离头尾越近的数据,性能越高,中间最慢。
  • 每个结点需要创建node对象:由此决定了其最高性能无法超越数组。

应用场景

  • 默认添加性能不如ArrayList,查询性能更不如,所以在大众场景无法取代ArrayList。
  • 擅长在头部及尾部操作,真正适用的场景是栈和队列,即先进后出和先进先出,它们只处理头部和尾部数据,而不会去处理中间的数据。比如生产者和消费者模式,如不了解请自行百度。

Deque

LinkedList实现了Deque接口,专门用于处理栈和队列的场景。
  • 双端队列的常用方法 
		//声明双端队列 
		Deque deque = new LinkedList();
		deque.addFirst(1);	//从队列头部添加数据
		deque.addLast(1);	//从队列尾部添加数据
		deque.pollFirst();	//从队列头部取出数据
		deque.pollFirst();	//从队列尾部取出数据
  • 栈的常用方法 
		//声明栈
		Deque deque = new LinkedList();
		deque.push(1);	//从栈顶添加数据
		deque.pop();	//从栈顶取出数据

如上,这两种场景内部结构仍然是LinkedList,性能特点和前面说的一样。

【Java集合夜话】第5篇:ArrayList与LinkedList的双子星,一文吃透数据结构的艺术 ArrayList和LinkedList作为Java集合框架中最常用的两种List实现,在实际开发中经常被用到。本文深入对比ArrayList和LinkedList的底层实现原理、性能特点、内存模型、CPU缓存影响、实际应用场景以及面试重点。通过源码分析、性能测试和丰富的代码示例,帮助你掌握如何在项目中正确选择和使用这两种数据结构。文章同时提供了详细的性能优化建议,是Java开发者必读的技术文章。 阅读详情

相关推荐

对ArrayList和LinkedList底层实现原理详解

今天小编就为大家分享一篇对ArrayList和LinkedList底层实现原理详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

疑案追踪:Spring Boot内存泄露排查记

总第323篇2019年 第001篇 在项目迁移到Spring Boot之后,发生内存使用量过高的问题。本文介绍了整个排查过程以及使用到的工具,也非常适用于其他堆外内...

美团技术团队 3131

机器学习实战案例:使用随机森林/XGBoost等模型进行分类预测,提高银行营销活动效率

1. 项目背景介绍 在这篇文章中,我们将使用 Python 搭建逻辑回归(Logistic Regression),随机森林(Random Forest),XGBoosting,Bagging,KNN (K-Nearest Neighbors) ,神经网络(Neural Network)等6种机器学习/深度学习模型,对某个银行的营销活动数据集进行分类预测,尝试找出那些潜在客户。 银行通常会打电话给一些潜在客户来销售一些存款/投资产品,这些投资产品会使银行获得更多利润以及资金的灵活性,因此银行希望能定位那些会

Yann_YU的博客 9300

【JAVA集合篇】LinkedList详解

LinkedList和ArrayList数据结构是完全不一样的,ArrayList 底层是数组的结构,而 LinkedList 的底层则是链表的结构, 它可以进行高效的插入和移除的操作,它基于的是一个双向链表的结构。

初念初恋的博客 1222

PWM与光耦的完美联姻:低成本4-20mA转换方案揭秘

本文揭秘了PWM与光耦结合的低成本4-20mA转换方案,适用于工业自动化领域。通过PWM信号控制输出电流,光耦实现电气隔离,该方案成本低廉、电路简单且隔离可靠。文章详细介绍了两种经典电路实现方案,包括四光耦线性模式方案和PWM控制LM317方案,并提供了实战技巧与优化策略,帮助中小企业和电子DIY爱好者构建经济高效的4-20mA转换系统。

weixin_29229055的博客 539

【数据分析/商业分析】面试题整理——SQL专题

数据分析\商业分析—面试题整理 自己总结的SQL类的面试题 文章目录数据分析\商业分析—面试题整理1.新增用户2.活跃用户数量3.连续登录4.次(n)日留存5.每个科目下分数最高的两名学生

awater_17的博客 1859

ArrayList vs. LinkedList:数据结构之争

ArrayList,LinkedList

todoitbo的博客 1万+

避坑指南:RK3588上编译EtherCAT 1.6时遇到的权限问题和模块安装错误

本文详细解析了在RK3588平台上编译EtherCAT 1.6时常见的权限问题和模块安装错误,提供了两种实战解决方案和嵌入式开发的通用权限管理策略。通过优化内核配置和编译参数,显著提升实时性能,帮助开发者高效完成EtherCAT主站部署。

weixin_33724046的博客 455

ArrayList和Linklist解析(清晰易懂)

ArrayList底层数组默认初始化容量为 101、jdk1.8 中ArrayList 底层先创建一个长度为0的数组2、当第一次添加元素 (调用 add0方法) 时,会初始化为一个长度为 10 的数组。

weixin_56380175的博客 1529

从零到一:PrimeTime静态时序分析入门指南

本文详细介绍了PrimeTime静态时序分析(STA)的入门指南,从基础概念到实际应用全面覆盖。内容包括时序路径分类、PrimeTime环境搭建、时序约束设置与验证、时序分析与报告解读等核心内容,帮助工程师快速掌握STA技术,解决数字芯片设计中的时序收敛问题。

weixin_28730131的博客 269

STM32 HAL库驱动BMP388:从寄存器配置到高精度气压温度采集

本文详细介绍了如何使用STM32 HAL库驱动BMP388气压温度传感器,实现高精度数据采集。内容涵盖从硬件连接到STM32CubeMX配置、关键寄存器解析、驱动源码编写,到核心补偿算法与海拔换算的完整流程,并提供了针对不同应用场景的优化配置与调试技巧,帮助开发者快速构建稳定可靠的测量方案。

weixin_29299807的博客 171

Java数据结构——5.链表(LinkedList)

比特就业课Java数据结构——链表(LinkedList) 课后思维总结

要努力去发光,而不是被照亮~ 597

BiSeNet:面向实时语义分割的双边网络

BiSeNet 系列重新定义了实时语义分割的范式“不要在速度和精度之间妥协——用架构创新,两者兼得。V1:提出双边解耦思想,启发后续大量工作;V2:通过极致轻量化,证明小模型也能高精度;V3(工业版):推动端侧 AI 落地,成为行业标准之一。如今,BiSeNet 已成为嵌入式视觉系统的默认选择,其设计理念(解耦、轻量、融合)被广泛应用于目标检测、姿态估计等领域。

浩瀚之水的专栏 268

JAVA数据结构 DAY5-LinkedList

本文系统讲解了Java中的链表数据结构与LinkedList实现,主要内容包括:1. 对比ArrayList与LinkedList的底层差异,分析链表结构的优势;2. 详细解析单链表和双向循环链表的实现原理,提供完整代码示例;3. 精选10道链表经典面试题,涵盖快慢指针、虚拟头节点等核心解题技巧;4. 深入讲解Java集合框架中LinkedList的使用方法,包括List和Deque接口操作;5. 总结ArrayList与LinkedList的核心区别及适用场景。

HRDMN的博客 898

Python 实现生命游戏及进化模拟完整指南

生命模拟是进化计算的一个特定子集,模拟了自然界中所观察到的自然过程,例如粒子或鸟群的聚集方式。生命模拟只是用来探索和优化问题的模拟形式之一,还有很多其他形式的模拟,可以更好地建模各种过程,但它们都源于康威生命游戏 (Conway’s Game of Life)。在本节中,我们将介绍生命模拟的基本概念,并使用 Python 实现康威生命游戏。

盼小辉丶的博客 7万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

5、数组循环左移——Linkedlist(java数据结构)

数组循环左移——Linkedlist 本题要求实现一个对数组进行循环左移的简单函数:一个数组a中存有n(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向左移m(≥0)个位置,即将a中的数据由(a0 a1​​ ⋯a​n−1)变换为(a​m ⋯an−1 a0 a1 ⋯am−1 )(最前面的m个数循环移至最后面的m个位置)。如果还需要考虑程序移动数据的次数尽量少,要如何设计移动的方法? 输入格式: 输入第1行给出正整数n(≤100)和整数m(≥0);第2行给出n个整数,其间以空格分隔。 输出格式

weixin_47202652的博客 598

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Java数据结构——5.链表(LinkedList)《干货笔记》

Java数据结构——5.链表(LinkedList)《干货笔记》

要努力去发光,而不是被照亮~ 815

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

java 16 - 5 LinkedList模拟栈数据结构的集合

  请用LinkedList模拟栈数据结构的集合,并测试     题目的意思是:     你自己的定义一个集合类,在这个集合类内部可以使用LinkedList模拟。 1 package cn_LinkedList; 2 3 import java.util.LinkedList; 4 5 6 7 8 public class MyStack ...

weixin_30608503的博客 69

1.4.5 Java数据结构章 LinkedList

在使用迭代器的过程中,it.hasNext()方法不涉及指针的移动,只是判断当前指针是否超出下标,也就是判断是否还有下一元素。而it.next()方法从源码来看则是先将指针下移并且取得当前元素。整个过程中只有next()方法涉及指针的下移.迭代器是LinkedList类的内部类,可以用这个类实现迭代的效果,这个类继承了ListIterator。it.next本质是先获取当前元素,在移动指针到下一个元素。**使用迭代器遍历—正向遍历 **

qq_40533485的博客 310

ArrayList和LinkedList:Java中的两种常见数据结构

ArrayList和LinkedList都是Java集合框架中的类。ArrayList是一个基于数组的实现,它可以动态调整大小以容纳更多的元素。LinkedList是一个基于链表的实现,它可以在任何位置插入或删除元素。

abzz的博客 668

Java8 ArrayList,LinkedList,Vector关键源码阅读

一,ArrayList 1,ArrayList底层数据结构 ArrayList的底层数据结构是一个数组,Object[] elementData。 以下是ArrayListde一些主要参数 // 默认数组长度 10 private static final int DEFAULT_CAPACITY = 10; // 空实例的数组 private static final Object[] EMPTY_ELEMENTDATA = {}; // 初始化默

bai_ye_的博客 210

ArrayList和LinkedList的区别

关于List中的常用集合框架的具体实现类无非就是ArrayList以及LinkedList。而关于两者的区别也是在面试中较为高频的考点。 区别如下: 两者的底层数据结构不同,ArrayList的底层数据结构使用的是数组,而LinkedList的底层数据结构使用的是链表。 LinkedList还实现了Deque接口,说明LinkedList还可以作为队列使用。需要注意的是。这个队列还是双端队列。 public class LinkedList<E> extends AbstractSe

m0_46415190的博客 146

ArrayList和LinkedList实现

在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;Java基础,ArrayList和LinkedList在实际中已经使用很多了,探究一下他的底层实现。在add 中出现了modCount++,在 AbstractList中定义,

zl5186888的博客 384

ArrayList 和 LinkedList 源码解析

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

男儿不展风云志,空有天生八尺躯 350

ArrayList 底层实现和LinkedList 底层实现方式

1. ArrayList 底层实现 ①ArrayList 通过数组实现,一旦我们实例化 ArrayList 无参数构造函数默认为数组初始化长度为 10②add 方法底层实现如果增加的元素个数超过了 10 个,那么 ArrayList 底层会新生成一个数组,长度为原数组的 1.5 倍+1,然后将原数组的内容复制到新数组当中,并且后续增加的内容都会放到新数组当中。当新数组无法容纳增加的元素时,重复该过程。是一旦数组超出长度,就开始扩容数组。扩容数组调用的方法 Arrays.copyOf(objArr, obj

weixin_62862983的博客 639

ArrayList和LinkedList

<br />1:事先能预知元素数量时,应优先选择 ArrayList,并且在构造中进行初始化<br />2:事先不能预知元素数量时,根据不同的迭代需要选择 ArrayList 或者 LinkedList<br />3:如果有很多的 remove 操作时,应优先选择 LinkedList<br />4:需要顺序迭代,也就是从第一个元素开始一个一个地访问到最后一个时,应优先选择 LinkedList<br />5:需要随机访问,也就是使用 get(int) 方法取任意下标访问时,应优先选择 ArrayList

小灰灰的部落 315
上一篇: Java数据结构(4)-ArrayList
下一篇: Java数据结构(6)-ArrayDeque
free_java
博客等级 码龄10年 664粉丝 131原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值