原创

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

  上一篇文章我们分析了几种遍历集合的方式,虽然遍历方式的写法有多种,但是底层无非就是for循环和foreach(迭代器)两种方式!这两种遍历方式都能达到遍历集合的效果,但是在效率上孰优孰劣?我们先看一段代码:

public static void main(String[] args) {
   List<String> linkedList = new LinkedList<>();
   for (int i = 0; i < 100000; i++) {
      linkedList.add("L1");
   }
   long time1 = System.currentTimeMillis();
   //for遍历
   for (int i = 0; i < linkedList.size(); i++) {
      System.out.println(linkedList.get(i));
   }
   long time2 = System.currentTimeMillis();
   //foreach遍历
   for (String s : linkedList) {
      System.out.println(s);
   }
   long time3 = System.currentTimeMillis();
   System.out.println("for执行时间:" + (time2 - time1));
   System.out.println("foreach执行时间:" + (time3 - time2));
}

  这次我们创建的是一个LinkedList,通过循环往集合里面写入了十万个元素,然后分别通过foreach(迭代器)和for的方式对集合进行遍历输出,并且记录了两种方式的耗时。那我们先思考一下哪种方式的耗时更长呢?我们看下结果:

  

  我们能看到使用for遍历的耗时是foreach的好几倍!如果想知道为什么会有这么大的差距,我们只能通过源码找答案了!其实差距不是在循环本身,而是在循环内获取元素的时候!我们先看下使用for循环时的linkedList.get(i) 的实现:

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

  LinkedList的get方法内部先调用了checkElementIndex方法检查了index参数的合法性,然后调用node(index)方法获取元素。我们进入node方法:

Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
    Node<E> x = first;
    for (int i = 0; i < index; i++)
        x = x.next;
    return x;
} else {
    Node<E> x = last;
    for (int i = size - 1; i > index; i--)
        x = x.prev;
    return x;
}

}

  从上面代码我们能分析出下面几点特性

1、LinkedListd底层是一个双向链表,可以从头部遍历也可以从尾部遍历,这样做可以提高获取元素的效率!
2、当获取元素的索引小于集合元素数量的一半,则从队列头部开始遍历直到目标元素!如果大于一半则从队列尾部开始遍历直到目标元素!
3、每次在获取元素的时候,都需要从队列头或者队列尾部逐个遍历直到找到目标元素。这也就是为什么使用for循环遍历LinkedList慢的原因!并且,当集合的元素数量越多,for遍历与foreach的差距就越大!

  那么如果我们的集合类型是ArrayList,使用for和foreach的差距大吗?由于ArrayList底层是数组,通过索引获取数组的效率是非常高的,因此使用for与foreach在效率基本没太大差别!

正文到此结束
Loading...