31

我读到增强的 for 循环比这里的普通for 循环更有效:

http://developer.android.com/guide/practices/performance.html#foreach

当我搜索它们的效率差异时,我发现的只是:在正常的for循环的情况下,我们需要一个额外的步骤来找出数组的长度或大小等,

for(Integer i : list){
   ....
}


int n = list.size();
for(int i=0; i < n; ++i){
  ....
}

但这是唯一的原因,增强的 for 循环比普通的 for 循环更好吗?在这种情况下,最好使用普通的 for 循环,因为理解增强的 for 循环有点复杂。

检查这个有趣的问题: http: //www.coderanch.com/t/258147/java-programmer-SCJP/certification/Enhanced-Loop-Vs-Loop

谁能解释这两种for循环的内部实现,或者解释使用增强for循环的其他原因?

4

7 回答 7

58

说增强的 for 循环更有效有点过于简单化了。可以,但在许多情况下,它几乎与老式循环完全相同。

首先要注意的是,对于集合,增强的 for 循环使用 an Iterator,因此如果您使用 an 手动迭代集合,Iterator那么您应该具有与增强的 for 循环几乎相同的性能。

增强的 for 循环比简单实现的传统循环更快的一个地方是这样的:

LinkedList<Object> list = ...;

// Loop 1:
int size = list.size();
for (int i = 0; i<size; i++) {
   Object o = list.get(i);
   /// do stuff
}

// Loop 2:
for (Object o : list) {
  // do stuff
}

// Loop 3:
Iterator<Object> it = list.iterator();
while (it.hasNext()) {
  Object o = it.next();
  // do stuff
}

在这种情况下,Loop 1 将比 Loop 2 和 Loop 3 慢,因为它必须(部分)在每次迭代中遍历列表才能找到 position 处的元素i。然而,循环 2 和 3 由于使用了Iterator. 循环 2 和 3 也将具有几乎相同的性能,因为循环 3 几乎正是您在循环 2 中编写代码时编译器将产生的结果。

于 2012-07-19T07:00:01.963 回答
9

我自己对我今天用上述几点做的一个小实验感到惊讶。所以我所做的是我将一定数量的元素插入到链表中,并使用上述三种方法对其进行迭代 1)使用高级 for 循环 2)使用迭代器 3)使用简单循环和 get()
我猜程序员是这样的因为你们可以通过查看代码更好地理解我做了什么。

long advanced_timeElapsed,iterating_timeElapsed,simple_timeElapsed;
    long first=System.nanoTime();
    for(Integer i: myList){
        Integer b=i;
    }
    long end= System.nanoTime();
    advanced_timeElapsed=end-first;
    System.out.println("Time for Advanced for loop:"+advanced_timeElapsed);
    first=System.nanoTime();
    Iterator<Integer> it = myList.iterator();
    while(it.hasNext())
    {
        Integer b=it.next();
    }
    end= System.nanoTime();
    iterating_timeElapsed=end-first;
    System.out.println("Time for Iterating Loop:"+iterating_timeElapsed);
    first=System.nanoTime();
    int counter=0;
    int size= myList.size();
    while(counter<size)
    {
        Integer b=myList.get(counter);
        counter++;  
    }
    end= System.nanoTime();
    simple_timeElapsed=end-first;
    System.out.println("Time for Simple Loop:"+simple_timeElapsed);

结果出乎我的意料。以下是 3 种情况下经过的时间图。 经过时间的图表

Y 轴经过时间 X 轴测试用例test case1
:10 输入
test case2:30 输入
test case3:50 输入
test case4:100 输入
test case5:150 输入
test case6:300 输入
test case7:500 输入
test case8:1000 输入
测试用例9:2000 输入
测试用例10:5000 输入
测试用例11:10000 输入
测试用例12:100000 输入

在这里您可以看到简单的循环比其他循环执行得更好。如果您在上面的代码中发现任何错误,请回复,我会再次检查。在我挖掘字节码并查看引擎盖下发生的事情后,将进一步更新。对于这么长的回复,我深表歉意,但我喜欢描述性的。菲利普

于 2014-05-28T09:55:05.853 回答
7

我读到增强的 for 循环比普通的 for 循环更有效。

实际上有时它对程序的效率较低,但大多数时候它完全一样。

它对开发人员来说更高效,这通常更重要

在迭代集合时,for-each 循环特别有用。

List<String> list = 
for(Iterator<String> iter = list.iterator(); list.hasNext(); ) {
    String s = list.next();

更容易写成(但做同样的事情,所以它对程序没有效率)

List<String> list = 
for(String s: list) {

当通过索引访问随机访问的集合时,使用“旧”循环稍微更有效。

List<String> list = new ArrayList<String>(); // implements RandomAccess
for(int i=0, len = list.size(); i < len; i++) // doesn't use an Iterator!

在集合上使用 for-each 循环始终使用迭代器,这对于随机访问列表的效率略低。

AFAIK,使用 for-each 循环对程序来说永远不会更有效,但就像我说的那样,开发人员的效率往往更重要

于 2012-07-19T06:58:04.623 回答
3

for-each 使用 Iterator 接口。我怀疑它是否比“旧”样式更有效。迭代器还需要检查列表的大小。

它主要是为了可读性。

对于像 LinkedList 这样的非随机访问集合,它应该更快,但是这样比较是不公平的。无论如何,您不会习惯于第二次实现(索引访问速度较慢)。

于 2012-07-19T06:59:42.297 回答
3

foreach 循环与这种循环一样有效:

for (Iterator<Foo> it = list.iterator(); it.hasNext(); ) {
     Foo foo = it.next();
     ...
}

因为它是严格等价的。

如果您使用

int size = list.size();
for (int i = 0; i < size; i++) {
    Foo foo = list.get(i);
    ...
}

然后 foreach 循环将具有与您的循环等效的性能,但仅适用于 ArrayList。在 LinkedList 的情况下,您的循环将具有糟糕的性能,因为在每次迭代时,它都必须遍历列表的所有节点,直到它到达i第 th 元素。

foreach 循环(或基于迭代器的循环,相同)没有这个问题,因为迭代器保持对当前节点的引用,并在每次迭代时简单地转到下一个节点。这是最糟糕的选择,因为它适用于所有类型的列表。它还更清楚地表达了意图,并且更安全,因为您不会冒险增加循环内的索引,或者在嵌套循环的情况下使用错误的索引。

于 2012-07-19T07:02:36.677 回答
1

所有答案都很好,我认为不需要更多答案,但我只想指出:

增强的 for 语句只能用于obtain array elements

不能习惯modify elements。_ _

如果您的程序需要修改元素,请使用传统的计数器控制 for语句。

看一看

int[] array = { 1, 2, 3, 4, 5 };
for (int counter = 0; counter < array.length; counter++)
    array[counter] *= 2;

我们无法使用该enhanced for语句,因为我们是modifying the array’s elements.

于 2017-03-30T08:32:38.577 回答
0

从有效的Java:

循环遍历数组的标准习语不一定会导致冗余检查。现代 JVM 实现对它们进行了优化。

但是 Josh Bloch 没有描述 JVM 如何优化它们。

于 2012-07-19T09:50:01.217 回答