您的位置:首页 > 其它

List遍历:for,foreach Iterator 速度比较

2018-03-01 15:40 465 查看
结论:
如果是ArrayList,用三种方式遍历的速度是for>Iterator>foreach,速度级别基本一致; 
如果是LinkedList,则三种方式遍历的差距很大了,数据量大时越明显(一般是超过100000级别),用for遍历的效率远远落后于foreach和Iterator,Iterator>foreach>>>for; 
模拟100000条数据,放入ArrayList和LinkedList,对两个List分别用三种方式进行遍历,耗时如下图所示: public class Test {

public static void main(String[] args) {
// 初始化
List<String> arrList = new ArrayList<String>();
List<String> linkList = new LinkedList<String>();
for (int i = 0; i < 100000; i++) {
arrList.add( String.valueOf( i ) );
linkList.add( String.valueOf( i ) );
}
System.out.println( "---------------------测试结果------------------------" );
System.out.println( "for arrList 时间是 \t" + testFor( arrList ) );
System.out.println( "iterator arrList 时间是 \t" + testIterator( arrList ) );
System.out.println( "Foreach arrList 时间是 \t" + testForeach( arrList ) );
System.out.println( "---------------------------------------------------" );
System.out.println( "for linkList 时间是 \t" + testFor( linkList ) );
System.out.println( "iterator linkList 时间是 \t" + testIterator( linkList ) );
System.out.println( "Foreach linkList 时间是 \t" + testForeach( linkList ) );
}

public static long testFor(List<String> list) {
long startTime = 0L, endTime = 0L;
String str;
startTime = System.nanoTime();
for (int i = list.size() - 1; i >= 0; i--) {
str = list.get( i );
}
endTime = System.nanoTime();
return endTime - startTime;
}

public static long testIterator(List<String> list) {
long startTime = 0L, endTime = 0L;
String str;
startTime = System.nanoTime();
Iterator<String> it = list.iterator();
while (it.hasNext()) {
str = it.next();
}
endTime = System.nanoTime();
return endTime - startTime;

}

public static long testForeach(List<String> list) {
long startTime = 0L, endTime = 0L;
String str;
startTime = System.nanoTime();
for (String string : list) {
str=string;
}
endTime = System.nanoTime();
return endTime - startTime;

}探索原因: 
1:测试发现foreach和Iterator基本上都在一个速度级别,但Iterator会稍稍快于foreach,事实上,foreach就是基于Iterator实现的,可通过反编译工具看到。下面两段代码效果是一样的:
// foreach
for(Object obj : list){
System.out.println(obj);
}

// Iterator
Iterator<Object> iterator = list.iterator();
while(iterator.hasNext()){
Object obj = iterator.next();
System.out.println(obj);
}
1
2
3
4
5
6
7
8
9
10
11
所以foreach和Iterator基本是效率相当的,慢的时间猜测就是foreach隐式转换成Iterator所消耗的时间.

2:接下来要解释的是为什么ArrayList的遍历中for比Iterator快,而LinkedList中却是Iterator远快于for?这得从ArrayList和LinkedList两者的数据结构说起了: 
ArrayList是基于索引(index)的数组,索引在数组中搜索和读取数据的时间复杂度是O(1),但是要增加和删除数据却是开销很大的,因为这需要重排数组中的所有数据。 
LinkedList的底层实现则是一个双向循环带头节点的链表,因此LinkedList中插入或删除的时间复杂度仅为O(1),但是获取数据的时间复杂度却是O(n)。 
明白了两种List的区别之后,就知道,ArrayList用for循环随机读取的速度是很快的,因为ArrayList的下标是明确的,读取一个数据的时间复杂度仅为O(1)。但LinkedList若是用for来遍历效率很低,读取一个数据的时间复杂度就达到了为O(n)。而用Iterator的next()则是顺着链表节点顺序读取数据的效率就很高了。最后总结: 
1:ArrayList用三种遍历方式都差得不算太多,一般都会用for或者foreach,因为Iterator写法相对复杂一些。
2:LinkedList的话,推荐使用foreach或者Iterator(数据量越打时,三者方法差别明显)。 
Effective Java中建议,一般情况下使用foreach进行循环,因为其在简洁性和预防Bug方面有着传统for循环无法比拟的优势,并且没有性能损失。但是除了一下三种情况: 
过滤:如果需要遍历集合,并删除选定的元素,就需要使用显式的迭代器,以便可以调用它的remove方法。
转换:如果需要遍历列表或数组,并取代它部分或者全部的元素值,就需要列表迭代器ListIterator或者数组索引,以便设定元素的值。(如果直接更改它引用对象的值的话,也可以使用Iterator,前提是符合按引用传递的原则,Iterator的元素为基本数据类型就不会按引用传递,或者它们的包装类,因为是不可变类,也不符合要求。)
平行迭代:如果需要并行地遍历多个集合,就需要显式的控制迭代器或者索引变量,以便所有迭代器或者索引变量都可以得到同步前移。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: