Java for 循环和 for-each 循环之间是否存在性能差异?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/256859/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 12:06:27  来源:igfitidea点击:

Is there a performance difference between a for loop and a for-each loop?

javaperformancefor-loop

提问by eflles

What, if any, is the performance difference between the following two loops?

如果有的话,以下两个循环之间的性能差异是什么?

for (Object o: objectArrayList) {
    o.DoSomething();
}

and

for (int i=0; i<objectArrayList.size(); i++) {
    objectArrayList.get(i).DoSomething();
}

采纳答案by Vijay Dev

From Item 46 in Effective Javaby Joshua Bloch :

来自Joshua Bloch 的Effective Java第 46 条:

The for-each loop, introduced in release 1.5, gets rid of the clutter and the opportunity for error by hiding the iterator or index variable completely. The resulting idiom applies equally to collections and arrays:

// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
    doSomething(e);
}

When you see the colon (:), read it as “in.” Thus, the loop above reads as “for each element e in elements.” Note that there is no performance penalty for using the for-each loop, even for arrays. In fact, it may offer a slight performance advantage over an ordinary for loop in some circumstances, as it computes the limit of the array index only once. While you can do this by hand (Item 45), programmers don't always do so.

1.5 版中引入的 for-each 循环通过完全隐藏迭代器或索引变量来消除混乱和出错的机会。由此产生的习语同样适用于集合和数组:

// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
    doSomething(e);
}

当您看到冒号 (:) 时,将其读为“in”。因此,上面的循环读作“对于元素中的每个元素 e”。请注意,使用 for-each 循环没有性能损失,即使是数组也是如此。事实上,在某些情况下,它可能比普通的 for 循环提供轻微的性能优势,因为它只计算一次数组索引的限制。虽然您可以手动执行此操作(第 45 条),但程序员并不总是这样做。

回答by Paul Tomblin

Even with something like an ArrayList or Vector, where "get" is a simple array lookup, the second loop still has additional overhead that the first one doesn't. I would expect it to be a tiny bit slower than the first.

即使使用 ArrayList 或 Vector 之类的东西,其中“get”是一个简单的数组查找,第二个循环仍然有第一个循环没有的额外开销。我希望它比第一个慢一点。

回答by Jouni K. Sepp?nen

The only way to know for sure is to benchmark it, and even that is not as simple as it may sound. The JIT compiler can do very unexpected things to your code.

确定知道的唯一方法是对其进行基准测试,即使这听起来也不那么简单。JIT 编译器可以对您的代码做非常意想不到的事情。

回答by Zach Scrivena

The for-each loop should generally be preferred. The "get" approach may be slower if the List implementation you are using does not support random access. For example, if a LinkedList is used, you would incur a traversal cost, whereas the for-each approach uses an iterator that keeps track of its position in the list. More information on the nuances of the for-each loop.

通常应该首选 for-each 循环。如果您使用的 List 实现不支持随机访问,则“get”方法可能会更慢。例如,如果使用 LinkedList,则会产生遍历成本,而 for-each 方法使用迭代器来跟踪其在列表中的位置。有关for-each 循环细微差别的更多信息。

I think the article is now here: new location

我认为文章现在在这里: 新位置

The link shown here was dead.

此处显示的链接已失效。

回答by P Arrayah

All these loops do the exact same, I just want to show these before throwing in my two cents.

所有这些循环都完全相同,我只想在投入我的两分钱之前展示这些。

First, the classic way of looping through List:

一、循环List的经典方式:

for (int i=0; i < strings.size(); i++) { /* do something using strings.get(i) */ }

Second, the preferred way since it's less error prone (how many times have YOU done the "oops, mixed the variables i and j in these loops within loops" thing?).

其次,首选方式,因为它不太容易出错(你做了多少次“哎呀,在循环内的这些循环中混合变量 i 和 j”的事情?)。

for (String s : strings) { /* do something using s */ }

Third, the micro-optimized for loop:

三、微优化的for循环:

int size = strings.size();
for (int i = -1; ++i < size;) { /* do something using strings.get(i) */ }

Now the actual two cents: At least when I was testing these, the third one was the fastest when counting milliseconds on how long it took for each type of loop with a simple operation in it repeated a few million times - this was using Java 5 with jre1.6u10 on Windows in case anyone is interested.

现在实际的两美分:至少当我测试这些时,第三个是最快的,当计算每种类型的循环花费的时间时,第三个是最快的,其中一个简单的操作重复了几百万次 - 这是使用 Java 5如果有人感兴趣,在 Windows 上使用 jre1.6u10。

While it at least seems to be so that the third one is the fastest, you really should ask yourself if you want to take the risk of implementing this peephole optimization everywhere in your looping code since from what I've seen, actual looping isn't usually the most time consuming part of any real program (or maybe I'm just working on the wrong field, who knows). And also like I mentioned in the pretext for the Java for-each loop(some refer to it as Iterator loopand others as for-in loop) you are less likely to hit that one particular stupid bug when using it. And before debating how this even can even be faster than the other ones, remember that javac doesn't optimize bytecode at all (well, nearly at all anyway), it just compiles it.

虽然至少看起来第三个是最快的,但你真的应该问问自己,你是否愿意冒险在循环代码中的任何地方实现这种窥视孔优化,因为从我所看到的,实际循环不是'这通常是任何实际程序中最耗时的部分(或者我可能只是在错误的领域工作,谁知道呢)。而且就像我在 Java for-each 循环的借口中提到的(有些人将其称为Iterator 循环,而另一些人将其称为for-in 循环),您在使用它时不太可能遇到那个特定的愚蠢错误。在讨论这甚至可以比其他的更快之前,请记住 javac 根本不优化字节码(好吧,无论如何几乎都没有),它只是编译它。

If you're into micro-optimization though and/or your software uses lots of recursive loops and such then you may be interested in the third loop type. Just remember to benchmark your software well both before and after changing the for loops you have to this odd, micro-optimized one.

如果您正在进行微优化和/或您的软件使用大量递归循环等,那么您可能会对第三种循环类型感兴趣。请记住在将 for 循环更改为这个奇怪的、微优化的循环之前和之后都对您的软件进行良好的基准测试。

回答by Steve Kuo

It's always better to use the iterator instead of indexing. This is because iterator is most likely optimzied for the List implementation while indexed (calling get) might not be. For example LinkedList is a List but indexing through its elements will be slower than iterating using the iterator.

最好使用迭代器而不是索引。这是因为迭代器最有可能针对 List 实现进行了优化,而索引(调用 get)可能不是。例如 LinkedList 是一个列表,但通过其元素进行索引将比使用迭代器进行迭代要慢。

回答by Fortyrunner

foreach makes the intention of your code clearer and that is normally preferred over a very minor speed improvement - if any.

foreach 使您的代码的意图更清晰,并且通常比非常小的速度改进更受欢迎 - 如果有的话。

Whenever I see an indexed loop I have to parse it a little longer to make sure it does what I thinkit does E.g. Does it start from zero, does it include or exclude the end point etc.?

每当我看到一个索引循环时,我都必须对其进行更长时间的解析,以确保它执行我认为的操作,例如它是否从零开始,是否包含或排除端点等?

Most of my time seems to be spent reading code (that I wrote or someone else wrote) and clarity is almost always more important than performance. Its easy to dismiss performance these days because Hotspot does such an amazing job.

我的大部分时间似乎都花在阅读代码(我写的或其他人写的)上,清晰几乎总是比性能更重要。现在很容易忽略性能,因为 Hotspot 做得如此出色。

回答by Sarmun

Well, performance impact is mostly insignificant, but isn't zero. If you look at JavaDoc of RandomAccessinterface:

嗯,性能影响大多是微不足道的,但不是零。如果您查看RandomAccess接口的JavaDoc :

As a rule of thumb, a List implementation should implement this interface if, for typical instances of the class, this loop:

for (int i=0, n=list.size(); i < n; i++)
    list.get(i);

runs faster than this loop:

for (Iterator i=list.iterator(); i.hasNext();)
      i.next();

根据经验,如果对于类的典型实例,这个循环,List 实现应该实现这个接口:

for (int i=0, n=list.size(); i < n; i++)
    list.get(i);

运行速度比这个循环快:

for (Iterator i=list.iterator(); i.hasNext();)
      i.next();

And for-each loop is using version with iterator, so for ArrayListfor example, for-each loop isn't fastest.

并且 for-each 循环使用带有迭代器的版本,因此ArrayList例如,for-each 循环并不是最快的。

回答by Wibowit

The following code:

以下代码:

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

interface Function<T> {
    long perform(T parameter, long x);
}

class MyArray<T> {

    T[] array;
    long x;

    public MyArray(int size, Class<T> type, long x) {
        array = (T[]) Array.newInstance(type, size);
        this.x = x;
    }

    public void forEach(Function<T> function) {
        for (T element : array) {
            x = function.perform(element, x);
        }
    }
}

class Compute {
    int factor;
    final long constant;

    public Compute(int factor, long constant) {
        this.factor = factor;
        this.constant = constant;
    }

    public long compute(long parameter, long x) {
        return x * factor + parameter + constant;
    }
}

public class Main {

    public static void main(String[] args) {
        List<Long> numbers = new ArrayList<Long>(50000000);
        for (int i = 0; i < 50000000; i++) {
            numbers.add(i * i + 5L);
        }

        long x = 234553523525L;

        long time = System.currentTimeMillis();
        for (int i = 0; i < numbers.size(); i++) {
            x += x * 7 + numbers.get(i) + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
        x = 0;
        time = System.currentTimeMillis();
        for (long i : numbers) {
            x += x * 7 + i + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
        x = 0;
        numbers = null;
        MyArray<Long> myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
        for (int i = 0; i < 50000000; i++) {
            myArray.array[i] = i * i + 3L;
        }
        time = System.currentTimeMillis();
        myArray.forEach(new Function<Long>() {

            public long perform(Long parameter, long x) {
                return x * 8 + parameter + 5L;
            }
        });
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(myArray.x);
        myArray = null;
        myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
        for (int i = 0; i < 50000000; i++) {
            myArray.array[i] = i * i + 3L;
        }
        time = System.currentTimeMillis();
        myArray.forEach(new Function<Long>() {

            public long perform(Long parameter, long x) {
                return new Compute(8, 5).compute(parameter, x);
            }
        });
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(myArray.x);
    }
}

Gives following output on my system:

在我的系统上提供以下输出:

224
-699150247503735895
221
-699150247503735895
220
-699150247503735895
219
-699150247503735895

I'm running Ubuntu 12.10 alpha with OracleJDK 1.7 update 6.

我正在使用 OracleJDK 1.7 更新 6 运行 Ubuntu 12.10 alpha。

In general HotSpot optimizes a lot of indirections and simple reduntant operations, so in general you shouldn't worry about them unless there are a lot of them in seqence or they are heavily nested.

一般来说,HotSpot 优化了很多间接和简单的冗余操作,所以一般你不应该担心它们,除非它们有很多顺序或者它们是严重嵌套的。

On the other hand, indexed get on LinkedList is much slower than calling next on iterator for LinkedList so you can avoid that performance hit while retaining readability when you use iterators (explicitly or implicitly in for-each loop).

另一方面,LinkedList 上的索引 get 比在 LinkedList 的迭代器上调用 next 慢得多,因此当您使用迭代器(在 for-each 循环中显式或隐式)时,您可以在保持可读性的同时避免性能下降。

回答by Changgeng

By the variable name objectArrayList, I assume that is an instance of java.util.ArrayList. In that case, the performance difference would be unnoticeable.

通过变量名称objectArrayList,我假设它是java.util.ArrayList. 在这种情况下,性能差异将是不明显的。

On the other hand, if it's an instance of java.util.LinkedList, the second approach will be much slower as the List#get(int)is an O(n) operation.

另一方面,如果它是 的实例java.util.LinkedList,则第二种方法会慢得多,因为List#get(int)是 O(n) 操作。

So the first approach is always preferred unless the index is needed by the logic in the loop.

所以第一种方法总是首选,除非循环中的逻辑需要索引。