ArrayList和LinkedList对比

1ArrayListLinkedList简介

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

2ArrayListLinkedList对比

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

 

   其中:for...size()方式和for...size variable方式便是使用get()对元素进行读取,当数据超过10000时差距已经相当明显。(源码见附录)

   如果有一个列表,要对其进行大量的插入和删除操作,在这种情况下LinkedList就是一个较好的选择。如果重复的在一个ArrayList内部插入、或删除一个元素已经存在的元素可能需要进行,这就意味着数据移动和复制上的开销。如果在LinkedList内部加入一个元素或从其中删除一个元素,只是简单的这个元素分配一个记录,然后调整两个节点。在LinkedList增加或删除一个元素的开销是固定的,而在ArrayList的增加一个元素的开销是与ArrayList的大小成比例的。

 

3总结

       ArrayListLinkedList在性能上各有优缺点,都有各自所适用的地方,总的说来可以描述如下: 
1.对ArrayListLinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加或删除元素,会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部对象进行节点指向的修改即可 
2.在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。
3LinkedList不支持高效的随机元素访问。 

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

 

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

   常见的遍历方式:for eachiterator迭代、普通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++){}

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

 
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 eachiterator的效率要明显高于for循环方式。

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

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

参考文档: http://docs.oracle.com/javase/8/docs/api/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值