ArrayList和LinkedList的区别
ArrayList和Vector使用了数组的实现,可以认为ArrayList或者Vector封装了对内部数组的操作,比如向数组中添加,删除,插入新的元素或者数据的扩展和重定向。
LinkedList使用了循环双向链表数据结构。与基于数组ArrayList相比,这是两种截然不同的实现技术,这也决定了它们将适用于完全不同的工作场景。
LinkedList链表由一系列表项连接而成。一个表项总是包含3个部分:元素内容,前驱表和后驱表。
在下图展示了一个包含3个元素的LinkedList的各个表项间的连接关系。在JDK的实现中,无论LikedList是否为空,链表内部都有一个header表项,它既表示链表的开始,也表示链表的结尾。表项header的后驱表项便是链表中第一个元素,表项header的前驱表项便是链表中最后一个元素
下面以增加和删除元素为例比较ArrayList和LinkedList的不同之处:
1
(1)增加元素到列表尾端:
在ArrayList中增加元素到队列尾端的代码如下:
public boolean add(E e){ensureCapacity(size+1);//确保内部数组有足够的空间elementData[size++]=e;//将元素加入到数组的末尾,完成添加return true;
}
ArrayList中add()方法的性能决定于ensureCapacity()方法。ensureCapacity()的实现如下:
public vod ensureCapacity(int minCapacity){modCount++;int oldCapacity=elementData.length;if(minCapacity>oldCapacity){ //如果数组容量不足,进行扩容Object[] oldData=elementData;int newCapacity=(oldCapacity*3)/2+1; //扩容到原始容量的1.5倍if(newCapacitty<minCapacity) //如果新容量小于最小需要的容量,则使用最小//需要的容量大小newCapacity=minCapacity ; //进行扩容的数组复制elementData=Arrays.copyof(elementData,newCapacity);}
}
可以看到,只要ArrayList的当前容量足够大,add()操作的效率非常高的。只有当ArrayList对容量的需求超出当前数组大小时,才需要进行扩容。扩容的过程中,会进行大量的数组复制操作。而数组复制时,最终将调用System.arraycopy()方法,因此add()操作的效率还是相当高的。
LinkedList 的add()操作实现如下,它也将任意元素增加到队列的尾端:
public boolean add(E e){addBefore(e,header);//将元素增加到header的前面return true;
}
其中addBefore()的方法实现如下:
private Entry<E> addBefore(E e,Entry<E> entry){Entry<E> newEntry = new Entry<E>(e,entry,entry.previous);newEntry.provious.next=newEntry;newEntry.next.previous=newEntry;size++;modCount++;return newEntry;
}
可见,LinkeList由于使用了链表的结构,因此不需要维护容量的大小。从这点上说,它比ArrayList有一定的性能优势,然而,每次的元素增加都需要新建一个Entry对象,并进行更多的赋值操作。在频繁的系统调用中,对性能会产生一定的影响。
1
(2)增加元素到列表任意位置
除了提供元素到List的尾端,List接口还提供了在任意位置插入元素的方法:void add(int index,E element);
由于实现的不同,ArrayList和LinkedList在这个方法上存在一定的性能差异,由于ArrayList是基于数组实现的,而数组是一块连续的内存空间,如果在数组的任意位置插入元素,必然导致在该位置后的所有元素需要重新排列,因此,其效率相对会比较低。
以下代码是ArrayList中的实现:
public void add(int index,E element){if(index>size||index<0)throw new IndexOutOfBoundsException("Index:"+index+",size: "+size);ensureCapacity(size+1);System.arraycopy(elementData,index,elementData,index+1,size-index);elementData[index] = element;size++;
}
可以看到每次插入操作,都会进行一次数组复制。而这个操作在增加元素到List尾端的时候是不存在的,大量的数组重组操作会导致系统性能低下。并且插入元素在List中的位置越是靠前,数组重组的开销也越大。
而LinkedList此时显示了优势:
public void add(int index,E element){addBefore(element,(index==size?header:entry(index)));
}
可见,对LinkedList来说,在List的尾端插入数据与在任意位置插入数据是一样的,不会因为插入的位置靠前而导致插入的方法性能降低。
1
(3)删除任意位置元素
对于元素的删除,List接口提供了在任意位置删除元素的方法:
public E remove(int index);
对ArrayList来说,remove()方法和add()方法是雷同的。在任意位置移除元素后,都要进行数组的重组。ArrayList的实现如下:
public E remove(int index){RangeCheck(index);modCount++;E oldValue=(E) elementData[index];int numMoved=size-index-1;if(numMoved>0)System.arraycopy(elementData,index+1,elementData,index,numMoved);elementData[--size]=null;return oldValue;
}
可以看到,在ArrayList的每一次有效的元素删除操作后,都要进行数组的重组。并且删除的位置越靠前,数组重组时的开销越大。
public E remove(int index){return remove(entry(index));
}
private Entry<E> entry(int index){if(index<0 || index>=size)throw new IndexOutBoundsException("Index:"+index+",size:"+size);Entry<E> e= header;if(index<(size>>1)){//要删除的元素位于前半段for(int i=0;i<=index;i++)e=e.next;}else{for(int i=size;i>index;i--)e=e.previous;}return e;
}
在LinkedList的实现中,首先要通过循环找到要删除的元素。如果要删除的位置处于List的前半段,则从前往后找;若其位置处于后半段,则从后往前找。因此无论要删除较为靠前或者靠后的元素都是非常高效的;但要移除List中间的元素却几乎要遍历完半个List,在List拥有大量元素的情况下,效率很低。
1
(4)容量参数
容量参数是ArrayList和Vector等基于数组的List的特有性能参数。它表示初始化的数组大小。当ArrayList所存储的元素数量超过其已有大小时。它便会进行扩容,数组的扩容会导致整个数组进行一次内存复制。因此合理的数组大小有助于减少数组扩容的次数,从而提高系统性能。
public ArrayList(){this(10);
}
public ArrayList (int initialCapacity){super();if(initialCapacity<0)throw new IllegalArgumentException("Illegal Capacity:"+initialCapacity)this.elementData=new Object[initialCapacity];
}
ArrayList提供了一个可以制定初始数组大小的构造函数:
public ArrayList(int initialCapacity)
现以构造一个拥有100万元素的List为例,当使用默认初始化大小时,其消耗的相对时间为125ms左右,当直接制定数组大小为100万时,构造相同的ArrayList仅相对耗时16ms。
1
(5)遍历列表
遍历列表操作是最常用的列表操作之一,在JDK1.5之后,至少有3中常用的列表遍历方式:forEach操作,迭代器和for循环。
String tmp;
long start=System.currentTimeMills(); //ForEach
for(String s:list){tmp=s;
}
System.out.println("foreach spend:"+(System.currentTimeMills()-start));
start = System.currentTimeMills();
for(Iterator<String> it=list.iterator();it.hasNext();){ tmp=it.next();
}
System.out.println("Iterator spend;"+(System.currentTimeMills()-start));
start=System.currentTimeMills();
int size=;list.size();
for(int i=0;i<size;i++){ tmp=list.get(i);
}
System.out.println("for spend;"+(System.currentTimeMills()-start));
构造一个拥有100万数据的ArrayList和等价的LinkedList,使用以上代码进行测试,可以看到,最简便的ForEach循环并没有很好的性能表现,综合性能不如普通的迭代器,而是用for循环通过随机访问遍历列表时,ArrayList表项很好,但是LinkedList的表现却无法让人接受,甚至没有办法等待程序的结束。这是因为对LinkedList进行随机访问时,总会进行一次列表的遍历操作。性能非常差,应避免使用。
相关文章:
ArrayList和LinkedList的区别
ArrayList和Vector使用了数组的实现,可以认为ArrayList或者Vector封装了对内部数组的操作,比如向数组中添加,删除,插入新的元素或者数据的扩展和重定向。 LinkedList使用了循环双向链表数据结构。与基于数组ArrayList相比…...

记录 vue3 webpack 使用 iframe 遇到的坑
需求 我尝试用Vue3写一个自己的主页,把常用的功能集中到主页中,如下图 后发现一个好玩的东西,js实现的在网页底部出现鱼和波浪,如下图,就像想也放到自己的主页中,搜索后发现可以在Vue中用iframe标签直接引…...
华为OD机试真题 Java 实现【去除多余空格】【2023Q1 100分】
一、题目描述 去除文本多余空格,但不去除配对单引号之间的多余空格。给出关键词的起始和结束下标,去除多余空格后刷新关键词的起始和结束下标。 条件约束: 不考虑关键词起始和结束位置为空格的场景;单词的的开始和结束下标保证涵盖一个完整的单词,即一个坐标对开始和结束…...

SAP-MM 条件类型字段解析
01、“定价类型”:定义此条件类型的代码和描述,代码不能重复,描述可更改,根据实际需要,条件类型可定制; 02、“存取顺序”:表示此条件类型在定价时,要到存取顺序号定义的条件表中读…...

C#,码海拾贝(28)——求解“对称正定方程组”的“平方根法”之C#源代码
using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 求解线性方程组的类 LEquations /// 原作 周长发 /// 改编 深度混淆 /// </summary> public static partial class LEquations { /// <summary> /…...

碳纤维单丝外径测试中的纳米分辨率激光衍射法解决方案
摘要:碳纤维单丝热膨胀系数是碳纤维复合材料设计、生产与可靠性和寿命评估的重要参数,本文针对单丝径向高温热膨胀系数测试这一难题提出了相应的解决方案。解决方案的核心内容是基于激光衍射法和高温辐射加热,并采用衍射轮廓拟合技术以及相应…...

服务(第三十二篇)nginx做缓存服务器
nginx作为缓存服务配置语法 1、proxy_cache_path 配置语法(即缓存路径配置语法) Syntax:proxy_cache_path path [levelslevels] [use_temp_pathon|off] keys_zonename:size [inactivetime] [max_sizesize] [manager_filesnumber] [manager_s…...

Java 集合、数组、字符串的相互转换(关于list.toArray(new String[0])的源码分析)
在 Java 中,可以通过以下方式实现集合、数组和字符串之间的相互转换。 一、集合和数组的相互转化 ①、将集合转为数组:(toArray 方法) List<String> list new ArrayList<>(); list.add("apple"); lis…...

Redis的全局命令及相关误区
Redis中所说的数据结构是针对key-value中的value而言的。主要的结构包括String、哈希表、列表、集合等等在redis中存在16个库,涉及到后期的集群搭建只能使用0号库最为方便 查看所有键(支持通配符) keys * keys S*返回当前数据库中的键总数 …...

C++核心编程—类和对象,类的三大特性——封装、继承、多态
纵有疾风起,人生不言弃。本文篇幅较长,如有错误请不吝赐教,感谢支持。 💬文章目录 一.类和对象的概念①什么是对象?②抽象和类1.类的基本概念2.类的声明与定义:3.对象的创建与使用 二.类的封装①为什么有封…...

keep-alive 是 Vue 内置的一个组件,被用来缓存组件实例。
文章目录 简介注意点使用 keep-alive 有以下优缺点优点缺点 简介 keep-alive 是 Vue 内置的一个组件,被用来缓存组件实例。 使用 keep-alive 包裹动态组件时,被包裹的组件实例将会被缓存起来,而不会被销毁,直到 keep-alive 组件…...

(八)Spring之IOC控制反转、DI依赖注入介绍和使用(详解)
文章目录 前言SpringSpring IOC 简介BeanIOC 概述IOC 本质理解 Spring IOC 应用IOC xml装配IOC 依赖注入IOC Bean的作用域 IoC 自动装配Bean 的自动装配注解实现自动装配 IoC 使用注解开发模拟实现Spring IoC 前言 “Spring”在不同的上下文中表示不同的事物。它可以用来引用 …...

凸缺陷 convexityDefects
获取凸包,可以参考我的这篇文章: 凸包(Convex Hull)代码实现案例 获取了凸包之后,可以干什么呢? 凸缺陷凸包与轮廓之间的部分称为凸缺陷。凸缺陷可用来处理手势识别等问题。 通常情况下,使用如…...
c语言编程练习题:7-43 Shuffling Machine
Shuffling is a procedure used to randomize a deck of playing cards. Because standard shuffling techniques are seen as weak, and in order to avoid “inside jobs” where employees collaborate with gamblers by performing inadequate shuffles, many casinos empl…...
ffmpeg enum AVChannel枚举解析
AVChannel枚举是在2022-12-20的提交中添加的,对应的版本号是5.1. 这个提交的描述是"avutil/channel_layout: add AVChannel enum and related functions"。 原型 typedef struct AVChannelCustom {enum AVChannel id;char name[16];void *opaque; } AVCh…...
invest模型教程
详情点击链接:invest模型教程——建议收藏 1.生态系统服务 2.InVEST模型 3.InVEST所需数据(分辨率、格式、投影系统等)、获取及标准化预处理 4.InVEST运行 5.ArcGIS工具支撑InVEST模型 5.1ArcGIS数据形式与数据格式、数据格式之间的相互转换…...

LinuxShell编程
Shell编程 Shell的概念介绍 命令解释器 Shell是命令解释器(command interpreter),是Unix操作系统的用户接口,程序从用户接口得到输入信息,shell将用户程序及其输入翻译成操作系统内核(kernel)能够识别的指令&#x…...

stm32学习笔记-11 SPI通信
11 SPI通信 文章目录 11 SPI通信11.1 SPI通信协议11.2 W25Q64简介11.3 实验:软件SPI读写W25Q6411.4 SPI通信外设11.5 实验:硬件SPI读写W25Q64 注:笔记主要参考B站 江科大自化协 教学视频“ STM32入门教程-2023持续更新中”。 注:…...

“微商城”项目(3页面布局)
1.设置标题 设置页面头部标题,方便告诉用户当前显示的是哪一个页面。编辑src\router.js文件,示例代码如下。 routes: [{ path: /, redirect: /home, meta: { title: 首页 } },{ path: /home, component: Home, name: home, meta: { title: 首页 } } ] …...
Java 八股文 - MySQL
MySQL 1. MySQL 有几种锁? 三种锁的特点 表级锁:开销小,加锁快;不会出现死锁;锁定颗粒度大,发生锁冲突的概率最高,并发度最低。行级锁:开销大,加锁慢;会…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南 背景介绍完整操作步骤1. 创建Docker容器环境2. 验证GUI显示功能3. 安装ROS Noetic4. 配置环境变量5. 创建ROS节点(小球运动模拟)6. 配置RVIZ默认视图7. 创建启动脚本8. 运行可视化系统效果展示与交互技术解析ROS节点通…...

【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)
旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据!该数据集源自2025年4月发表于《地理学报》的论文成果…...