ArrayList和Vector使用了数组的实现,可以认为ArrayList或者Vector封装了对内部数组的操作,比如向数组中添加,删除,插入新的元素或者数据的扩展和重定向。
LinkedList使用了循环双向链表数据结构。与基于数组ArrayList相比,这是两种截然不同的实现技术,这也决定了它们将适用于完全不同的工作场景。
LinkedList链表由一系列表项连接而成。一个表项总是包含3个部分:元素内容,前驱表和后驱表,如图所示:
在下图展示了一个包含3个元素的LinkedList的各个表项间的连接关系。在JDK的实现中,无论LikedList是否为空,链表内部都有一个header表项,它既表示链表的开始,也表示链表的结尾。表项header的后驱表项便是链表中第一个元素,表项header的前驱表项便是链表中最后一个元素。
下面以增加和删除元素为例比较ArrayList和LinkedList的不同之处:
(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对象,并进行更多的赋值操作。在频繁的系统调用中,对性能会产生一定的影响。
(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的尾端插入数据与在任意位置插入数据是一样的,不会因为插入的位置靠前而导致插入的方法性能降低。
(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拥有大量元素的情况下,效率很低。
(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。
(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进行随机访问时,总会进行一次列表的遍历操作。性能非常差,应避免使用。
分享到:
相关推荐
1,ArrayList是数组的数据结构,LinkedList是链表的数据结构。 2,随机访问的时候,ArrayList的效率比较高,因为LinkedList要移动指针,而ArrayList是基于 3,索引(index)的数据结构,可以直接映射到。 4,插入、...
【Java面试题】ArrayList和LinkedList区别
在 Java 中,ArrayList 和 LinkedList 是两种常用的集合类,它们各自具有不同的特性和适用场景,主要体现在数据结构、访问效率和操作性能上。 1. 数据结构: - ArrayList 实现了一个动态数组,它内部是一个 Object...
ArrayList LinkedList Vector 区别 ArrayList、LinkedList、Vector 是 Java 中常用的数据结构实现类,它们都实现了 List 接口,但它们在存储方式、性能、线程安全性等方面有着不同特点。 首先,ArrayList 和 ...
### 关于ArrayList与LinkedList的区别 在Java编程语言中,`ArrayList`与`LinkedList`都是`List`接口的具体实现类,用于存储元素集合。虽然它们都实现了同样的接口并且提供了相同的基本功能,但在内部实现机制、性能...
总的来说,理解ArrayList和LinkedList的基本特性和应用场景,以及如何处理与之相关的安全性问题,是Java程序员必备的知识。通过深入学习和实践,可以更好地利用这些数据结构提升程序效率和质量。
测试ArrayList和LinkedList的add方法
### ArrayList、Vector、LinkedList 的区别与用法详解 在Java编程中,选择合适的数据结构对于程序的性能至关重要。本文将深入探讨ArrayList、Vector和LinkedList三种集合类的特点与使用场景,帮助开发者更好地理解...
在Java编程语言中,ArrayList和LinkedList都是集合框架中两种重要的列表实现,它们分别基于不同的数据结构,具有不同的特性和性能特点。以下是对这两个类的详细分析: 1. 数据结构: - ArrayList是基于动态数组的...
在Java的集合框架中,ArrayList和LinkedList是两种常用的列表实现,它们都实现了List接口,但它们在内存管理和操作效率上存在显著差异。了解这些差异并根据具体应用场景选择合适的列表类型,能够有效提升J2EE应用...
05丨ArrayList还是LinkedList?使用不当性能差千倍.html
ArrayList、LinkedList和Vector是三种常见的动态数组实现,它们各自有特定的特性和使用场景。这里我们将深入探讨这三个类的性能对比,以及它们在不同操作下的表现。 ArrayList是基于动态数组实现的,它提供了随机...
ArrayList和LinkedList是Java集合框架中两种重要的动态数组实现,它们都是List接口的实现类,但它们在存储和操作数据方面有着显著的区别。本文件"arraylist-linkedlist-test.zip"主要探讨了在执行添加和删除元素操作...
在Java编程语言中,ArrayList、LinkedList和Vector是三种常见的动态数组实现,它们都在java.util包中,用于存储和管理对象的集合。这三个类都实现了List接口,提供了多种操作方法,但它们在内部实现和性能特性上有所...
《ArrayList与LinkedList源码解析》 在Java编程中,ArrayList和LinkedList是两种常见的动态数组,它们都是Java集合框架的一部分,提供了对元素存储和操作的功能。本篇将深入探讨ArrayList和LinkedList的内部实现...
10.ArrayList 和LinkedList的区别.avi
- **实现原理**:Vector与ArrayList类似,也是基于动态数组,但它是线程安全的。 - **线程安全**:每个公共方法都在内部进行了同步,这意味着在多线程环境下无需额外的同步代码。 - **效率**:由于线程安全的实现...
ArrayList和LinkedList的比较与实现原理 在 Java 中,ArrayList 和 LinkedList 是两个常用的集合类,它们都是 List 接口的实现类,但它们之间有着鲜明的区别。今天,我们将深入探讨这两个集合类的实现原理和比较。 ...
Java 中 ArrayList 与 LinkedList 对比详情 在 Java 中,ArrayList 和 LinkedList 是两个常用的集合实现方式,它们都实现了 Collection 接口,但是它们之间存在着一些关键的差异。本文将通过实例对比 Java 中 ...
二、`ArrayList`与`LinkedList`的区别 `ArrayList`和`LinkedList`都是`List`接口的实现,但它们在内部实现和性能上有所不同: 1. 内存结构:`ArrayList`基于动态数组实现,元素存储在连续的内存空间;`LinkedList`...