JAVA容器之List
来源:互联网 发布:apache网站服务器架设 编辑:程序博客网 时间:2024/06/11 18:41
List的实现类主要是ArrayList和LinkedList,两个主要的差别是ArrayList是通过数组实现的,但LinkedList是通过链表实现。
可以想象,ArrayList在随机访问效率上远高于LinkedList,因为LinkedList访问一个元素必须从头节点开始依次访问知道找到目标节点,所以时间复杂度为O(n),而ArrayList的随机访问复杂度几乎是O(1)。
但频繁进行插入删除数据的时候,LinkedList的效率远高于ArrayList,因为LinedList只需要改变指针(引用)的内容,时间复杂度为O(1),而ArrayList添加与删除需要大量移动元素,时间复杂度为O(n)。
遍历效率分析
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比用迭代器也要快一些。这是为什么呢?
1、 为什么ArrayList循环遍历比迭代器遍历快?
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()也需要额外的时间开销,所以这样遍历自然会比较满一些。
2、 为什么用迭代器遍历ArrayList比LinkedList快一些呢?
这个问题从源码角度还真不好说,搜了一些资料,发现大家对这个效率原因答案都不是特别明确,依稀看到有人的解释我觉得还是蛮有道理的,就是ArrayList的数据结构内存分配是连续的,而LinkedList是不连续的,访问下一个元素的时候需要通过地址去查找,所以有了额外的时间开销。
操作Arrays.asList()
有这么一使用Arrays.asList的例子:
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的差异,主要就差别在随机访问与修改,所以建议如下:
1、 如果需要大量删除与添加元素,使用LinkedList
2、 如果需要大量随机访问元素,使用ArrayList
3、 如果需要经常遍历,两者均可,ArrayList效率略高,但是如果选用LinedList,在遍历时尽量使用迭代器,否则相当于随机存取,效率非常低。
- JAVA容器之List
- java容器之List接口
- Java 基础 容器之List
- Java容器学习之List接口
- java容器之Array转换List
- java容器之Set,Map,List
- Java容器学习之——List
- 【JAVA之容器】3.List接口
- java容器之Array转换List
- java基础-数据容器之集合List
- STL容器之list
- STL容器之list
- 顺序容器之list
- STL容器之list
- C++容器之list
- STL容器之list
- STL容器之list
- STL之list容器
- 从MVC框架看MVC架构的设计
- GCD介绍
- sqlite3使用
- C++编程思想——运行时类型识别
- File io流创建文件
- JAVA容器之List
- other
- 计算机是如何工作之简单的程序分析
- android中IntentService和Service有什么区别
- BZOJ 2938 Poi2000 病毒 AC自动机+拓扑排序
- Java学习之线程总结
- SVN 命令
- CSDN测试Windows Live Writer离线写博客
- JDK,JRE,JVM的区别与联系