for与foreach在遍历集合时的差别你竟然不知道?

17coding技术博客

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的方式对集合进行遍历输出,并且记录了两种方式的耗时。那我们先思考一下哪种方式的耗时更长呢?我们看下结果:

  

f3a39beab7b3f64db38c6835cf9eaff3.webp

  我们能看到使用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;
8else {
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在效率基本没太大差别!


浏览 5
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报