当前位置: 首页 > news >正文

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使用了数组的实现&#xff0c;可以认为ArrayList或者Vector封装了对内部数组的操作&#xff0c;比如向数组中添加&#xff0c;删除&#xff0c;插入新的元素或者数据的扩展和重定向。 LinkedList使用了循环双向链表数据结构。与基于数组ArrayList相比&#xf…...

记录 vue3 webpack 使用 iframe 遇到的坑

需求 我尝试用Vue3写一个自己的主页&#xff0c;把常用的功能集中到主页中&#xff0c;如下图 后发现一个好玩的东西&#xff0c;js实现的在网页底部出现鱼和波浪&#xff0c;如下图&#xff0c;就像想也放到自己的主页中&#xff0c;搜索后发现可以在Vue中用iframe标签直接引…...

华为OD机试真题 Java 实现【去除多余空格】【2023Q1 100分】

一、题目描述 去除文本多余空格,但不去除配对单引号之间的多余空格。给出关键词的起始和结束下标,去除多余空格后刷新关键词的起始和结束下标。 条件约束: 不考虑关键词起始和结束位置为空格的场景;单词的的开始和结束下标保证涵盖一个完整的单词,即一个坐标对开始和结束…...

SAP-MM 条件类型字段解析

01、“定价类型”&#xff1a;定义此条件类型的代码和描述&#xff0c;代码不能重复&#xff0c;描述可更改&#xff0c;根据实际需要&#xff0c;条件类型可定制&#xff1b; 02、“存取顺序”&#xff1a;表示此条件类型在定价时&#xff0c;要到存取顺序号定义的条件表中读…...

C#,码海拾贝(28)——求解“对称正定方程组”的“平方根法”之C#源代码

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 求解线性方程组的类 LEquations /// 原作 周长发 /// 改编 深度混淆 /// </summary> public static partial class LEquations { /// <summary> /…...

碳纤维单丝外径测试中的纳米分辨率激光衍射法解决方案

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

服务(第三十二篇)nginx做缓存服务器

nginx作为缓存服务配置语法 1、proxy_cache_path 配置语法&#xff08;即缓存路径配置语法&#xff09; Syntax&#xff1a;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 中&#xff0c;可以通过以下方式实现集合、数组和字符串之间的相互转换。 一、集合和数组的相互转化 ①、将集合转为数组&#xff1a;&#xff08;toArray 方法&#xff09; List<String> list new ArrayList<>(); list.add("apple"); lis…...

Redis的全局命令及相关误区

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

C++核心编程—类和对象,类的三大特性——封装、继承、多态

纵有疾风起&#xff0c;人生不言弃。本文篇幅较长&#xff0c;如有错误请不吝赐教&#xff0c;感谢支持。 &#x1f4ac;文章目录 一.类和对象的概念①什么是对象&#xff1f;②抽象和类1.类的基本概念2.类的声明与定义&#xff1a;3.对象的创建与使用 二.类的封装①为什么有封…...

keep-alive 是 Vue 内置的一个组件,被用来缓存组件实例。

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

(八)Spring之IOC控制反转、DI依赖注入介绍和使用(详解)

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

凸缺陷 convexityDefects

获取凸包&#xff0c;可以参考我的这篇文章&#xff1a; 凸包&#xff08;Convex Hull&#xff09;代码实现案例 获取了凸包之后&#xff0c;可以干什么呢&#xff1f; 凸缺陷凸包与轮廓之间的部分称为凸缺陷。凸缺陷可用来处理手势识别等问题。 通常情况下&#xff0c;使用如…...

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的提交中添加的&#xff0c;对应的版本号是5.1. 这个提交的描述是"avutil/channel_layout: add AVChannel enum and related functions"。 原型 typedef struct AVChannelCustom {enum AVChannel id;char name[16];void *opaque; } AVCh…...

invest模型教程

详情点击链接&#xff1a;invest模型教程——建议收藏 1.生态系统服务 2.InVEST模型 3.InVEST所需数据&#xff08;分辨率、格式、投影系统等&#xff09;、获取及标准化预处理 4.InVEST运行 5.ArcGIS工具支撑InVEST模型 5.1ArcGIS数据形式与数据格式、数据格式之间的相互转换…...

LinuxShell编程

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

stm32学习笔记-11 SPI通信

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

“微商城”项目(3页面布局)

1.设置标题 设置页面头部标题&#xff0c;方便告诉用户当前显示的是哪一个页面。编辑src\router.js文件&#xff0c;示例代码如下。 routes: [{ path: /, redirect: /home, meta: { title: 首页 } },{ path: /home, component: Home, name: home, meta: { title: 首页 } } ] …...

Java 八股文 - MySQL

MySQL 1. MySQL 有几种锁&#xff1f; ​ 三种锁的特点 表级锁&#xff1a;开销小&#xff0c;加锁快&#xff1b;不会出现死锁&#xff1b;锁定颗粒度大&#xff0c;发生锁冲突的概率最高&#xff0c;并发度最低。行级锁&#xff1a;开销大&#xff0c;加锁慢&#xff1b;会…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器

一、原理介绍 传统滑模观测器采用如下结构&#xff1a; 传统SMO中LPF会带来相位延迟和幅值衰减&#xff0c;并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF)&#xff0c;可以去除高次谐波&#xff0c;并且不用相位补偿就可以获得一个误差较小的转子位…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter

java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用&#xff08;Math::max&#xff09; 2 函数接口…...

【版本控制】GitHub Desktop 入门教程与开源协作全流程解析

目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork&#xff08;创建个人副本&#xff09;步骤 2: Clone&#xff08;克隆…...