7

我刚开始学习 Java,我遇到的第一件事是foreach循环,不知道它的工作方式我做的第一件事是:

int[] array = new int [10];
for (int i: array){
    i = 1;
}

并且显然未能分配1给数组的每个元素。然后我将System.out.print(i);(after i = 1;) 添加到循环体中,并看到屏幕的输出是,1111111111但是由于在循环内做某事i是有效的,很可能i是数组每个元素的副本,不是吗?(第一个问题)

如果上述情况属实,这是否意味着foreach循环比常见的 for 循环慢得多,因为它涉及复制数组的每个元素?或者由于Java没有指针和指针运算,oprator[]可能以其他“糟糕”的方式设计,复制每个元素实际上更快?

如果上述假设成立,为什么要使用明显较慢foreach的循环而不是普通for循环?

简而言之,问题:

  • i每个元素的副本array吗?如果不是,那是什么?

  • 循环不是foreach比普通循环慢吗?如果不是,那么operator[]设计有多“糟糕”?

  • 除了可读性之外,没有什么能在foreach循环中获胜?

4

4 回答 4

11

在代码中

for (int i: array){

您声明一个变量i,在每次循环迭代时获取数组中下一个元素的值,它不是对该元素的引用。

i = 1;

您将新值分配给变量,而不是数组中的元素。

您不能直接使用 foreach 循环设置数组元素的值。为此使用正常for循环

for (int i = 0; i < array.length; i++) {
    array[i] = ...; // some value
}

在上面的示例中,您使用声明的变量i作为数组中元素的索引。

array[i] 

正在访问可以修改其值的元素本身。


Ans 显然未能将 1 分配给数组的每个元素。我添加了 System.out.print(i); 到循环的主体并看到屏幕的输出是 1111111111 但是由于在循环内对 i 执行某些操作是有效的,因此 i 很可能是数组每个元素的副本,不是吗?(第一个问题)

你必须把 the 放在 theSystem.out.print(i)之后i = 1,否则你会得到0000000

如果上述情况属实,这是否意味着 foreach 循环比普通 for 循环慢得多,因为它涉及复制数组的每个元素?或者由于 Java 没有指针和指针算法,所以 oprator[] 可能以其他“糟糕”的方式设计,即复制每个元素实际上更快?

看看这里,看看 foreach 循环是如何工作的。对于数组,

for (int i: array){
    i = 1;
}

相当于

for (int index = 0; index < array.length; index++) {
    int i = array[index];
    i = 1;
}

所以速度并不慢。您正在堆栈上进行另一项原始创建。

这取决于实施。对于数组,它不会以任何方式变慢。它只是用于不同的目的。

为什么要使用明显较慢的 foreach 循环而不是常见的 forloop?

一个原因是可读性。另一个是当您不关心更改数组的元素引用,而是使用当前引用时。

以引用类型为例

public class Foo {
    public int a;
}

Foo[] array = new Foo[3];
for (int i = 0; i < array.length; i++) {
    array[i] = new Foo();
    array[i].a = i * 17;
}

for (Foo foo : array) {
    foo.a = 0; // sets the value of `a` in each Foo object
    foo = new Foo(); // create new Foo object, but doesn't replace the one in the array
}

对于原始类型,这样的事情是行不通的。

for (int index = 0; index < array.length; index++) { 
    int i = array[index];
    i = 1; // doesn't change array[index]
}
于 2013-09-13T14:04:07.020 回答
7

来自 Joshua Bloch 在 Effective Java 中的第 46 条:

在 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 条),但程序员并不总是这样做。

优点

  1. 可读性
  2. 它增加了抽象级别 - 开发人员不必表达如何循环列表或数组(使用索引或迭代器)的低级细节,只需声明他们想要循环并且语言会处理其余的事情.

坏处:

无法访问索引或删除项目。

总结一下,

增强的 for 循环提供

  1. 一种简洁的高级语法,用于遍历列表或数组

    1.1 提高清晰度

    1.2 可读性。

    但是,它错过了:允许访问索引循环或删除项目。

于 2013-09-13T14:36:33.193 回答
3

通过引用设置值不起作用,因为int它是原始类型。对于任何类型的 Object[] 都可以。制作原始类型的副本非常快,并且处理器会在您没有意识到的情况下多次完成。

循环更具可读性,foreach但也更具可写性。一个常见的程序员错误是:

for (int i = 0; i < 10; i++)
{
    for(int j = 0; j < 10; i++) //oops, incrementing wrong variable!
    {
        //this will not execute as expected
    }
}

foreach使用循环是不可能犯这个错误的。

于 2013-09-13T14:14:22.370 回答
2

for 语句还有另一种形式,专为通过集合和数组进行迭代而设计。这种形式有时被称为增强的 for 语句,可用于使您的循环更加紧凑和易于阅读。 http://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html

所以,你是对的。可读性是这里的主要胜利。有关 for each 循环或增强型 for 循环的更多信息,请参见oralce 的博客条目

for each 使用了Iterable<E>接口的功能,因此性能取决于实现。

于 2013-09-13T14:26:15.517 回答