您的位置:首页 > 其它

ArrayList和LinkedList对比

2017-02-14 15:31 591 查看

1、ArrayList和LinkedList简介

        ArrayList和LinkedList均是Interface List<E>的实现。  

        ArrayList是一个数组列表,内部使用数组形式存放对象。因为Object是一切类型的父类,所以ArrayList中是有一个Object数组用来存放对象的。ArrayList常用的实现方法有add()、get()、set()、remove(),此外ArrayList内部还实现了iterator()用于迭代ArrayList内部元素以及listIterator()用于迭代ArrayList中的ListIterator。由于ArrayList所有的方法都是默认单线程下操作的,并不具有线程安全性,如果多个线程同时访问一个ArrayList实例,并且至少有一个线程修改表结构,它必须在外部自行封装同步方法或者在创建时就使用Collections.synchronizedList方法进行包括(形如: List
list = Collections.synchronizedList(new ArrayList(...)); )。

       LinkedList可以看做是一个双向链接的实现,每个元素都存放着Node<E> first节点和Node<E> last节点,LinkedList的add、remove、等均是通过移动节点指向实现,所以LinkedList进行插入和删除时不需要移动元素便可以实现。但是,LinkedList随机根据下标查询的效率跟元素的数量呈正相关且低于ArrayList。LinkedList也不具有线程安全性,如果多个线程同时访问一个链表,并且至少有一个线程修改表结构,它必须在外部自行封装同步方法或者在创建时就使用Collections.synchronizedList方法进行包括(形如:List
list = Collections.synchronizedList(new LinkedList(...)); )。

2、ArrayList和LinkedList对比

  由于ArrayList是数组实现,在使用get方法访问列表中的任意一个元素时,它的速度要比LinkedList快,复杂度O(1)。而LinkedList根据下标进行一次二分查找(如果下标小于size
/2 时,从头部查询,否则从尾部查询)后,从头部或尾部进行逐个查询对比复杂度可视为O(n/2)。且当数据超过一定量的时候ArrayList的优势就愈发的明显,下面有一张图可以看到相同数据量的ArrayList和LinkList的差距。
 


   其中:for...size()方式和for...size variable方式便是使用get()对元素进行读取,当数据超过10000时差距已经相当明显。(源码见附录)
  
如果有一个列表,要对其进行大量的插入和删除操作,在这种情况下LinkedList就是一个较好的选择。如果重复的在一个ArrayList内部插入、或删除一个元素,已经存在的元素可能需要进行移动,这就意味着数据移动和复制上的开销。如果在LinkedList内部加入一个元素或从其中删除一个元素,只是简单的为这个元素分配一个记录,然后调整两个节点。在LinkedList增加或删除一个元素的开销是固定的,而在ArrayList的增加一个元素的开销是与ArrayList的大小成比例的。
 

3、总结

       ArrayList和LinkedList在性能上各有优缺点,都有各自所适用的地方,总的说来可以描述如下: 

1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加或删除元素,会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部对象进行节点指向的修改即可。 

2.在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。

3.LinkedList不支持高效的随机元素访问。 

  当不经常添加或删除数据且需要大量随机地访问其中的元素时,使用ArrayList会提供比较好的性能;当添加或删除操作较多时,使用LinkedList或许是个更好的选择了。

 

附:List常见遍历方式性能比较

   常见的遍历方式:for each、iterator迭代、普通for循环:

for each 即增强型的for循环。for(type element: array ){}

for each丢掉了下标信息,如果使用for each且希望能够使用下标信息的话,则需要自定义计数器进行记录。

Iterator Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。ArrayList内部定义一个内部类Itr,该内部类实现Iterator接口。

for循环根据个人代码风格、代码修养、需求的不同还有两种方式:size()于循环体内和于循环体外。即:

int size=list.size();

for(int i=0;i<size;i++){}



for(int i=0;i<list.size();i++){}

根据以上说明我们尝试做了各个方式的不同数量size的ArrayList和LinkedList使用各种方法遍历时的耗时情况比较。

 
package com.fc.ssms;

import java.util.ArrayList;
import java.util.Formatter;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class Test {

static Formatter formatter = new Formatter(System.out);

public static void main(String[] args) {

formatter.format("%-20s %-20s %-20s %-20s %-25s\n", "数据量/类型", "foreach方式", "iterator方式", "for...size()方式",
"for...size variable方式");
//1、数据量为1000
int size = 1000;
List<String> arrayList = getArrayList(size);
List<String> linkedList = getLinkedList(size);
formatter.format("%-20s %-20s %-20s %-20s %-25s\n", size + "/ArrayList", foreach(arrayList) + "ms",
forIterator(arrayList) + "ms", for_size(arrayList) + "ms", for_size_variable(arrayList) + "ms");
formatter.format("%-20s %-20s %-20s %-20s %-25s\n", size + "/LinkedList", foreach(linkedList) + "ms",
forIterator(linkedList) + "ms", for_size(linkedList) + "ms", for_size_variable(linkedList) + "ms");
//2、数据量为10000
size = 10000;
arrayList = getArrayList(size);
linkedList = getLinkedList(size);
formatter.format("%-20s %-20s %-20s %-20s %-25s\n", size + "/ArrayList", foreach(arrayList) + "ms",
forIterator(arrayList) + "ms", for_size(arrayList) + "ms", for_size_variable(arrayList) + "ms");
formatter.format("%-20s %-20s %-20s %-20s %-25s\n", size + "/LinkedList", foreach(linkedList) + "ms",
forIterator(linkedList) + "ms", for_size(linkedList) + "ms", for_size_variable(linkedList) + "ms");
//3、数据量为100000
size = 100000;
arrayList = getArrayList(size);
linkedList = getLinkedList(size);
formatter.format("%-20s %-20s %-20s %-20s %-25s\n", size + "/ArrayList", foreach(arrayList) + "ms",
forIterator(arrayList) + "ms", for_size(arrayList) + "ms", for_size_variable(arrayList) + "ms");
formatter.format("%-20s %-20s %-20s %-20s %-25s\n", size + "/LinkedList", foreach(linkedList) + "ms",
forIterator(linkedList) + "ms", for_size(linkedList) + "ms", for_size_variable(linkedList) + "ms");
formatter.close();
}
/**
* 生成固定size的ArrayList
* @param size
* @return
*/
public static List<String> getArrayList(int size) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < size; i++) {
list.add("num=" + i);
}
return list;
}
/**
* 生成固定size的LinkedList
* @param size
* @return
*/
public static List<String> getLinkedList(int size) {
List<String> list = new LinkedList<String>();
for (int i = 0; i < size; i++) {
list.add("num=" + i);
}
return list;
}
/**
* foreach方式遍历
* @param list
* @return
*/
public static long foreach(List<String> list) {
long currentTime = System.currentTimeMillis();
for (String s : list) {
String temp = s;
}
return System.currentTimeMillis() - currentTime;
}
/**
* iterator方式遍历
* @param list
* @return
*/
public static long forIterator(List<String> list) {
long currentTime = System.currentTimeMillis();
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String temp = it.next();
}
return System.currentTimeMillis() - currentTime;
}

/**
* for遍历时内部判断size
* @param list
* @return
*/
public static long for_size(List<String> list) {
long currentTime = System.currentTimeMillis();
for (int i = 0; i < list.size(); i++) {
String temp = list.get(i);
}
return System.currentTimeMillis() - currentTime;
}

/**
* for遍历前将size置于变量中
* @param list
* @return
*/
public static long for_size_variable(List<String> list) {
long currentTime = System.currentTimeMillis();
int size = list.size();
for (int i = 0; i < size; i++) {
String temp = list.get(i);
}
return System.currentTimeMillis() - currentTime;
}

}




   可以看到在数据量低于10000时采用各个方式遍历ArrayList差别并不是很明显,而对于LinkedList的遍历for
each和iterator的效率要明显高于for循环方式。

   而数据在100000时采用for each和iterator分别遍历ArrayList和LinkedList区别依旧不是很也不至于很大,而此时采用for循环遍历LinkedList相比数据量在十万的时候耗时大大增加。且在遍历之前获取size()赋予变量中比每次调用size()获取size也会有一些提升。

   所以对List遍历在是可以尽量采取for each和Iterator方式进行,如果需要下标,则可以考虑使用计数器记录,如果遍历的确定是ArrayList时,则可以根据需求使用采取合适的遍历方式即可。

参考文档:http://docs.oracle.com/javase/8/docs/api/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linkedlist arraylist