java 为什么增强的for循环比普通的for循环更有效

bis0qfac  于 2023-01-04  发布在  Java
关注(0)|答案(7)|浏览(203)

我在这里读到 * 增强的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循环 * 的其他原因吗?

pgccezyw

pgccezyw1#

说增强的for循环更高效有点过于简单化了,它 * 可以 * 是这样,但在很多情况下它几乎和老式循环完全一样。
首先要注意的是,对于集合,增强的for循环使用Iterator,所以如果你使用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
}

在这种情况下,环路1将比环路2和环路3都慢,因为它必须在每次迭代中(部分地)遍历列表以找到位置i处的元素。然而,循环2和3将仅在列表中进一步步进一个元素,由于使用了Iterator,循环2和循环3也将具有几乎相同的性能,因为循环3几乎完全是编译器在循环2中编写代码时生成的结果。

mznpcxlj

mznpcxlj2#

我今天做了一个小实验,对上面提到的几点感到很惊讶,所以我做的是在链表中插入一定数量的元素,然后用上面提到的三种方法迭代: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种情况下所用时间的图表。x1c 0d1x
Y轴-经过时间X轴-测试用例
| 测试用例|输入|
| - ------|- ------|
| 1个|10个输入|
| 第二章|30个输入|
| 三个|50个输入|
| 四个|100个输入|
| 五个|150个输入|
| 六个|300个输入|
| 七|500个输入|
| 八个|1000个输入|
| 九|2000项投入|
| 十个|5000个输入|
| 十一|10000个输入|
| 十二|10万个输入|
在这里你可以看到简单的循环执行方式比其他更好。如果你发现上面的代码中有任何错误,请回复,我会再次检查。我会在通过字节码挖掘后进一步更新,看看引擎盖下发生了什么。我很抱歉这么长的回应,但我喜欢描述。菲利普

y1aodyip

y1aodyip3#

我读到增强的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) {

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

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循环总是使用Iterator,这对于随机访问列表来说效率稍低。
AFAIK,使用for-each循环对程序来说从来都不是更有效的,但是就像我说的,开发人员的效率通常要重要得多。

6rqinv9w

6rqinv9w4#

for-each使用Iterator接口,我怀疑它是否比“旧”风格更有效,Iterator还需要检查列表的大小。
主要是为了可读性。
对于非随机访问的集合,如LinkedList,它应该更快,但这样的比较是不公平的。无论如何,你不会习惯于第二个实现(索引访问很慢)。

brjng4g3

brjng4g35#

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个元素。
foreach循环(或者基于迭代器的循环,这是相同的),没有这个问题,因为迭代器保持对当前节点的引用,并且在每次迭代时简单地转到下一个节点。这是最好的选择,因为它对所有类型的列表都能很好地工作。它也更清楚地表达了意图,并且更安全,因为你不会冒险在循环中递增索引。或者在嵌套循环的情况下使用错误的索引。

whhtz7ly

whhtz7ly6#

所有的答案都是好的,我认为不需要更多的答案,但我只想指出:
增强的for语句只能用于obtain array elements
不能用于**modify elements
如果程序需要修改元素,请使用传统的counter-controlledfor语句。
看一看

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

4ktjp1zp

4ktjp1zp7#

来自有效Java:
循环数组的标准习惯用法不一定会导致冗余检查,现代JVM实现优化了它们。
但是Josh Bloch没有描述JVM如何优化它们。

相关问题