for与foreach在遍历集合时的差别你竟然不知道?
2020-07-12 20:15
上一篇文章我们分析了几种遍历集合的方式,虽然遍历方式的写法有多种,但是底层无非就是for循环和foreach(迭代器)两种方式!这两种遍历方式都能达到遍历集合的效果,但是在效率上孰优孰劣?我们先看一段代码:
1public static void main(String[] args) {
2 List linkedList = new LinkedList<>();
3 for (int i = 0; i < 100000; i++) {
4 linkedList.add("L1");
5 }
6 long time1 = System.currentTimeMillis();
7 //for遍历
8 for (int i = 0; i < linkedList.size(); i++) {
9 System.out.println(linkedList.get(i));
10 }
11 long time2 = System.currentTimeMillis();
12 //foreach遍历
13 for (String s : linkedList) {
14 System.out.println(s);
15 }
16 long time3 = System.currentTimeMillis();
17 System.out.println("for执行时间:" + (time2 - time1));
18 System.out.println("foreach执行时间:" + (time3 - time2));
19}
这次我们创建的是一个LinkedList,通过循环往集合里面写入了十万个元素,然后分别通过foreach(迭代器)和for的方式对集合进行遍历输出,并且记录了两种方式的耗时。那我们先思考一下哪种方式的耗时更长呢?我们看下结果:
我们能看到使用for遍历的耗时是foreach的好几倍!如果想知道为什么会有这么大的差距,我们只能通过源码找答案了!其实差距不是在循环本身,而是在循环内获取元素的时候!我们先看下使用for循环时的linkedList.get(i) 的实现:
1public E get(int index) {
2 checkElementIndex(index);
3 return node(index).item;
4}
LinkedList的get方法内部先调用了checkElementIndex方法检查了index参数的合法性,然后调用node(index)方法获取元素。我们进入node方法:
1Node node(int index) {
2// assert isElementIndex(index);
3if (index < (size >> 1)) {
4 Node x = first;
5 for (int i = 0; i < index; i++)
6 x = x.next;
7 return x;
8} else {
9 Node x = last;
10 for (int i = size - 1; i > index; i--)
11 x = x.prev;
12 return x;
13}
}
从上面代码我们能分析出下面几点特性
1、LinkedListd底层是一个双向链表,可以从头部遍历也可以从尾部遍历,这样做可以提高获取元素的效率!
2、当获取元素的索引小于集合元素数量的一半,则从队列头部开始遍历直到目标元素!如果大于一半则从队列尾部开始遍历直到目标元素!
3、每次在获取元素的时候,都需要从队列头或者队列尾部逐个遍历直到找到目标元素。这也就是为什么使用for循环遍历LinkedList慢的原因!并且,当集合的元素数量越多,for遍历与foreach的差距就越大!
那么如果我们的集合类型是ArrayList,使用for和foreach的差距大吗?由于ArrayList底层是数组,通过索引获取数组的效率是非常高的,因此使用for与foreach在效率基本没太大差别!