前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >什么情况用ArrayList or LinkedList呢?

什么情况用ArrayList or LinkedList呢?

作者头像
会呼吸的Coder
发布于 2020-08-19 12:55:36
发布于 2020-08-19 12:55:36
56100
代码可运行
举报
文章被收录于专栏:会呼吸的Coder会呼吸的Coder
运行总次数:0
代码可运行

ArrayList 和 LinkedList 是 Java 集合框架中用来存储对象引用列表的两个类。ArrayList 和 LinkedList 都实现 List 接口。先对List做一个简单的了解:

列表(list)是元素的有序集合,也称为序列。它提供了基于元素位置的操作,有助于快速访问、添加和删除列表中特定索引位置的元素。List 接口实现了 Collection 和 Iterable 作为父接口。它允许存储重复值和空值,支持通过索引访问元素。

读完这篇文章要搞清楚的问题:ArrayList和LinkedList有什么不同之处?什么时候应该用ArrayList什么时候又该用LinkedList呢?

下面以增加和删除元素为例比较ArrayList和LinkedList的不同之处

增加元素到列表尾端:

在ArrayList中增加元素到队列尾端的代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public boolean add(E e){
   ensureCapacity(size+1);//确保内部数组有足够的空间
   elementData[size++]=e;//将元素加入到数组的末尾,完成添加
   return true;      
} 

ArrayList中add()方法的性能决定于ensureCapacity()方法。ensureCapacity()的实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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()操作实现如下,它也将任意元素增加到队列的尾端:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public boolean add(E e){
   addBefore(e,header);//将元素增加到header的前面
   return true;
}

其中addBefore()的方法实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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对象,并进行更多的赋值操作。在频繁的系统调用中,对性能会产生一定的影响。

增加元素到列表任意位置

除了提供元素到List的尾端,List接口还提供了在任意位置插入元素的方法:void add(int index,E element);

由于实现的不同,ArrayList和LinkedList在这个方法上存在一定的性能差异,由于ArrayList是基于数组实现的,而数组是一块连续的内存空间,如果在数组的任意位置插入元素,必然导致在该位置后的所有元素需要重新排列,因此,其效率相对会比较低。

以下代码是ArrayList中的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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此时显示了优势:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void add(int index,E element){
   addBefore(element,(index==size?header:entry(index)));
}

可见,对LinkedList来说,在List的尾端插入数据与在任意位置插入数据是一样的,不会因为插入的位置靠前而导致插入的方法性能降低。

删除任意位置元素

对于元素的删除,List接口提供了在任意位置删除元素的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public E remove(int index);

对ArrayList来说,remove()方法和add()方法是雷同的。在任意位置移除元素后,都要进行数组的重组。ArrayList的实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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的每一次有效的元素删除操作后,都要进行数组的重组。并且删除的位置越靠前,数组重组时的开销越大。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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拥有大量元素的情况下,效率很低。

容量参数

容量参数是ArrayList和Vector等基于数组的List的特有性能参数。它表示初始化的数组大小。当ArrayList所存储的元素数量超过其已有大小时。它便会进行扩容,数组的扩容会导致整个数组进行一次内存复制。因此合理的数组大小有助于减少数组扩容的次数,从而提高系统性能。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public  ArrayList(){
  this(10);  
}
public ArrayList (int initialCapacity){
   super();
   if(initialCapacity<0)
       throw new IllegalArgumentException("Illegal Capacity:"+initialCapacity)
      this.elementData=new Object[initialCapacity];
}

ArrayList提供了一个可以制定初始数组大小的构造函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public ArrayList(int initialCapacity) 

现以构造一个拥有100万元素的List为例,当使用默认初始化大小时,其消耗的相对时间为125ms左右,当直接制定数组大小为100万时,构造相同的ArrayList仅相对耗时16ms。

遍历列表

遍历列表操作是最常用的列表操作之一,在JDK1.5之后,至少有3中常用的列表遍历方式:

  • forEach操作
  • 迭代器
  • for循环。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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在性能上各有优缺点,都有各自所适用的地方,总的说来可以描述如下:

1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。 对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配; 而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。 2.在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。 3.LinkedList不支持高效的随机元素访问。 4.ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 初级程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java 集合系列08: List总结(LinkedList, ArrayList等使用场景和性能分析)
这里写图片描述 (01) List 是一个接口,它继承于Collection的接口。它代表着有序的队列。 (02) AbstractList 是一个抽象类,它继承于AbstractCollection。AbstractList实现List接口中除size()、get(int location)之外的函数。 (03) AbstractSequentialList 是一个抽象类,它继承于AbstractList。AbstractSequentialList 实现了“链表中,根据index索引值操作链表的全部函数”。
好好学java
2019/09/19
7430
Java 集合系列08: List总结(LinkedList, ArrayList等使用场景和性能分析)
Java Collection Framework : List
List 是 Java Collection Framework的重要成员,具体包括List接口及其所有的实现类。由于List接口继承了Collection接口,所以List拥有Collection的所有操作。同时,又因为List是列表类型,所以List本身还提供了一些适合自身的方法。ArrayList 是一个动态数组,实现了数组动态扩容,随机访问效率高;LinkedList是一个双向链表,随机插入和删除效率高,可用作队列的实现。
heasy3
2020/08/01
9450
Java Collection Framework : List
java核心数据结构总结
 JDK提供了一组主要的数据结构的实现,如List、Set、Map等常用结构,这些结构都继承自java.util.collection接口。
Java编程指南
2019/08/02
4380
java核心数据结构总结
ArrayList 简介
ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
橘子君丶
2023/03/06
5010
ArrayList源码+扩容机制分析
ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
Vincent-yuan
2021/09/08
9050
数据结构之ArrayList
首先:讲述ArrayList之前先来说下List,List是java重要的数据结构之一,我们经常接触到的有ArrayList、Vector和LinkedList三种,他们都继承来自java.util.Collection接口
Qwe7
2022/04/01
2750
ArrayList详解
ArrayList 是一个数组列表。它的主要底层实现是Object数组,但与 Java 中的数组相比,它的容量能动态变化,可看作是一个动态数组结构。特别注意的是,当我们装载的是基本类型的数据 int,long,boolean,short,byte… 的时候,我们只能存储他们对应的包装类。
程序员阿杜
2023/08/25
2660
ArrayList源码解析(1)
ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
黑洞代码
2021/01/28
3400
ArrayList源码详解
ArrayList UML类图 ArrayList 概述 ArrayList 是实现 List 接口的动态数组,所谓动态就是它的大小是可变的。实现了所有可选列表操作,并允许包括 null 在内的所有
秋白
2018/05/24
5600
Java集合之ArrayList ?
ArrayList是最常见以及每个Java开发者最熟悉的集合类了,顾名思义,ArrayList就是一个以数组形式实现的集合,以一张表格来看一下ArrayList里面有哪些基本的元素:
技术从心
2019/08/06
4120
Java集合之ArrayList ?
Java之ArrayList解剖学
回归基础,回归原理,你会有更深的领悟。今天来聊一聊在Java当中常用的一个集合类:ArrayList。
23号杂货铺
2019/09/27
4110
Java之ArrayList解剖学
「 深入浅出 」集合List
第一篇文章 「 深入浅出 」java集合Collection和Map 主要讲了对集合的整体介绍,本篇文章主要讲List相对于Collection新增的一些重要功能以及其重要子类ArrayList、LinkedList、Vector
KEN DO EVERTHING
2019/01/17
5350
[搞懂Java集合类2]LinkedList和Queue
- Java集合类 今天我们来探索一下LinkedList和Queue,以及Stack的源码。 本文参考 http://cmsblogs.com/?p=155 和 https://www.jiansh
Java技术江湖
2019/09/25
5210
ArrayList和LinkendList不是我们想的那样?
集合作为我们日常开发中最常用的存储数据的容器,是开发过程中使用最频繁的对象类型之一,但是有多种集合类型,不同的集合类型的实现方式不同,使用的场景也不同。这里就比较一下ArrayList和LinkedList。
故里
2020/11/25
6310
ArrayList和LinkendList不是我们想的那样?
ArrayList、LinkedList和Vector的源码解析,带你走近List的世界
java.util.List接口是Java Collections Framework的一个重要组成部分,List接口的架构图如下:
李红
2019/09/04
3750
ArrayList
  ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
后端码匠
2019/09/30
1.2K0
这可能是最细的ArrayList详解了!
# 手撕ArrayList源码 > 文章首发于GitHub开源项目: [Java超神之路](https://github.com/shaoxiongdu/java-notes) ## ArrayList 简介 ArrayList 是一个数组列表。它的主要底层实现是`Object`数组,但与 Java 中的数组相比,它的**容量能动态变化**,可看作是一个动态数组结构。特别注意的是,当我们装载的是基本类型的数据 int,long,boolean,short,byte… 的时候,我们只能存储他们对应的包装
程序员阿杜
2021/09/11
9480
ArrayList源码简析
ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
大忽悠爱学习
2022/10/30
3480
集合框架2- ArrayList
其实 Java 集合框架也叫做容器,主要由两大接口派生而来,一个是 collection,主要存放对象的集合。另外一个是Map, 存储着键值对(两个对象)的映射表。
归思君
2023/10/16
1660
集合框架2- ArrayList
Java集合之ArrayList扩容机制
以无参数构造方式创建ArrayList时,实际上初始化赋值的是一个空数组(public ArrayList())。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为10
全栈程序员站长
2022/09/06
2940
Java集合之ArrayList扩容机制
相关推荐
Java 集合系列08: List总结(LinkedList, ArrayList等使用场景和性能分析)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验