List的实现类主要是ArrayList和LinkedList,两个主要的差异是ArrayList是经过数组实现的,但LinkedList是经过链表实现。
能够想象,ArrayList在随机访问效率上远高于LinkedList,由于LinkedList访问一个元素必须从头节点开始依次访问知道找到目标节点,因此时间复杂度为O(n),而ArrayList的随机访问复杂度几乎是O(1)。
但频繁进行插入删除数据的时候,LinkedList的效率远高于ArrayList,由于LinedList只须要改变指针(引用)的内容,时间复杂度为O(1),而ArrayList添加与删除须要大量移动元素,时间复杂度为O(n)。java
public class ListTest { List<Integer> array = new ArrayList<Integer>(); List<Integer> link = new LinkedList<Integer>(); private final int size; private long start, end; public ListTest(int size) { this.size = size; for (int i = 0; i < size; i++) { int t = (int) (Math.random() * 1000); array.add(t); link.add(t); } } public void ArrayIteratorTraversal() { Iterator<Integer> i = array.iterator(); int t; start = System.currentTimeMillis(); while (i.hasNext()) { t = i.next(); } end = System.currentTimeMillis(); System.out.println("ArrayList使用Iterator遍历" + size + "个数据花了" + (end - start) + "ms"); } public void LinkIteratorTraversal() { Iterator<Integer> i = link.iterator(); int t; start = System.currentTimeMillis(); while (i.hasNext()) { t = i.next(); } end = System.currentTimeMillis(); System.out.println("LinkedList使用Iterator遍历" + size + "个数据花了" + (end - start) + "ms"); } public void ArrayListIteratorTraversal() { ListIterator<Integer> i = array.listIterator(); int t; start = System.currentTimeMillis(); while (i.hasNext()) { t = i.next(); } end = System.currentTimeMillis(); System.out.println("ArrayList使用ListIterator遍历" + size + "个数据花了" + (end - start) + "ms"); } public void LinkListIteratorTraversal() { ListIterator<Integer> i = link.listIterator(); int t; start = System.currentTimeMillis(); while (i.hasNext()) { t = i.next(); } end = System.currentTimeMillis(); System.out.println("LinkedList使用ListIterator遍历" + size + "个数据花了" + (end - start) + "ms"); } public void ArrayCycleTraversal() { int t; start = System.currentTimeMillis(); for (int i = 0; i < size; i++) t = array.get(i); end = System.currentTimeMillis(); System.out.println("ArrayList使用循环遍历" + size + "个数据花了" + (end - start) + "ms"); } public void LinkCycleTraversal() { int t; start = System.currentTimeMillis(); for (int i = 0; i < size; i++) t = link.get(i); end = System.currentTimeMillis(); System.out.println("LinkedList使用循环遍历" + size + "个数据花了" + (end - start) + "ms"); } public static void main(String[] args) { ListTest t = new ListTest(10000000); System.out.println("*******开始*******"); t.ArrayIteratorTraversal(); t.LinkIteratorTraversal(); t.ArrayListIteratorTraversal(); t.LinkListIteratorTraversal(); t.ArrayCycleTraversal(); // t.LinkCycleTraversal(); System.out.println("*******结束*******"); } }
其中分别用了Iterator,ListIterator和循环来访问ArrayList和LinkedList,输出结果为
//输出结果
*开始*
ArrayList使用Iterator遍历100000个数据花了9ms
LinkedList使用Iterator遍历100000个数据花了9ms
ArrayList使用ListIterator遍历100000个数据花了8ms
LinkedList使用ListIterator遍历100000个数据花了9ms
ArrayList使用循环遍历100000个数据花了6ms
LinkedList使用循环遍历100000个数据花了8049ms
*结束*
//输出结果数组
使用迭代器时,ArrayList比LinkedList要快上很多,屡次测试大约是三倍的样子,Iterator和ListIterator遍历一样的容器效率差很少。可是经过循环遍历,LinkedList跟ArrayList差了三个数量级,其实用循环遍历,看似是顺序遍历List,可是,经过List.get()的方式过的元素值,实际上是经过随机访问来实现的,因此也从这个例子能够证实上面所说LinkedList的随机访问效率是很是差的。
因为数据再增加,最后一项LinkedList循环遍历的测试等待时间很是长,因此把这一项测试去掉,你们知道这个效率很是差就行了。咱们把测试数据规模扩大到10billion:
//输出结果
*开始*
ArrayList使用Iterator遍历10000000个数据花了37ms
LinkedList使用Iterator遍历10000000个数据花了121ms
ArrayList使用ListIterator遍历10000000个数据花了38ms
LinkedList使用ListIterator遍历10000000个数据花了117ms
ArrayList使用循环遍历10000000个数据花了30ms
*结束*
//输出结果
扩大数据规模后发现有一点是跟上一次测试结果相同的,就是ArrayList的效率明显比LinkedList要高一些,并且另外发现一个问题,用循环遍历ArrayList比用迭代器也要快一些。这是为何呢?
一、 为何ArrayList循环遍历比迭代器遍历快?markdown
public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; }
这是ArrayList中一个继承了Iterator的内部类的next()方法,看了源代码就能够发现其实ArrayList的Iterator就是直接访问ArryList中用于存放数据的一个数组,可是多了一些检查工做。并且,除此以外,遍历时每次iterator.hasNext()也须要额外的时间开销,因此这样遍历天然会比较满一些。
二、 为何用迭代器遍历ArrayList比LinkedList快一些呢?
这个问题从源码角度还真很差说,搜了一些资料,发现你们对这个效率缘由答案都不是特别明确,依稀看到有人的解释我以为仍是蛮有道理的,就是ArrayList的数据结构内存分配是连续的,而LinkedList是不连续的,访问下一个元素的时候须要经过地址去查找,因此有了额外的时间开销。数据结构
有这么一使用Arrays.asList的例子:dom
List<Integer>list=Arrays.asList(5,6,8,2,45,1,2,8,5,125,6,8,5); list.add(5);
经过Arrays.asList方法把一串数字转化成list,而后调用list中的add方法添加元素。可是运行会发现,编译器抛出了一个java.lang.UnsupportedOperationException异常,问题就在add方法,其实除了add方法,remove,retain等对list元素个数会产生变化的方法都是会报这个异常的,究其缘由,看一下Arrays.asList的源码
乍一看,发现Arrays.asList返回的就是一个ArrayList对象,可是仔细看发现这个ArrayList并不是java.util.ArrayList,而是Arrays的一个内部类,并且没有实现刚才说的add那些方法,而是直接继承了AbstractList,而后AbstractList并无实现add等方法,只是直接抛出了UnsupportedOperationException异常,因此才会有上述代码发生的那些状况。测试
正确的使用:大数据
List<Integer> list = new ArrayList<Integer>(Arrays.asList(5, 6, 8, 2, 45, 1, 2, 8, 5, 125, 6, 8, 5)); list.add(5);
对于LinekdList和ArrayList的差别,主要就差异在随机访问与修改,因此建议以下:
一、 若是须要大量删除与添加元素,使用LinkedList
二、 若是须要大量随机访问元素,使用ArrayList
三、 若是须要常常遍历,二者都可,ArrayList效率略高,可是若是选用LinedList,在遍历时尽可能使用迭代器,不然至关于随机存取,效率很是低。this