Foreach vs common for循环

时间:2013-09-13 14:02:37

标签: java foreach

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

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

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

如果上述情况属实,这并不意味着foreach循环比公共for循环要慢得多,因为它涉及制作数组中每个元素的副本?或者由于Java没有指针和指针算术,oprator[]可能会以其他“严重”的方式设计,复制每个元素实际上更快?

如果上述假设为真,为什么会使用明显较慢的foreach循环而不是常见的for循环呢?

简而言之:

  • i的每个元素的副本是array吗?如果不是这样的话 然后

  • foreach循环不是常见的慢吗?如果没有,怎么样 那么“很糟糕”是operator[]设计的吗?

  • foreach循环中的可读性外,没有其他内容了吗?

4 个答案:

答案 0 :(得分: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,但自从做了一些事情   循环内部有效,我很可能是每个元素的副本   阵列,不是吗? (第一个问题)

您必须将System.out.print(i)放在i = 1之后,否则您将获得0000000

  

如果上述情况属实,则不意味着foreach循环很多   慢于普通for循环,因为它涉及制作副本   数组的每个元素?或者因为Java没有指针和   指针运算,oprator []可能在其他一些设计   复制每个元素的“非常”时尚实际上更快?

看看here,了解foreach循环的工作原理。对于数组,

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

相当于

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

所以它并不慢。你正在堆栈上做一个更原始的创作。

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

  

为什么人们会使用明显较慢的foreach循环而不是常见的循环   for循环?

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

参考类型示例

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]
}

答案 1 :(得分:6)

来自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循环的性能损失,即使对于数组也是如此。在   事实上,它可能比普通的性能提供轻微的性能优势   在某些情况下循环,因为它计算数组的限制   索引只有一次。虽然你可以手工(第45项),程序员   不要总是这样做。

<强>优点

  
      
  1. 可读性
  2.   
  3. 它增加了抽象级别 - 而不是必须表达如何循环列表的低级细节   或者数组(使用索引或迭代器),开发人员只是说明了这一点   他们想要循环,而语言会照顾其余部分。
  4.   

<强>缺点:

  

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

总结一下,

  

增强的for循环提供

     
      
  1. 用于循环遍历列表或数组的简洁高级语法

         

    1.1提高了清晰度

         

    1.2可读性。

         

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

  2.   

答案 2 :(得分: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循环无法发出此错误。

答案 3 :(得分:2)

  

for语句还有另一个用于迭代集合和数组的表单。这个表单有时被称为增强for语句,可用于使循环更紧凑和易于阅读。    http://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html

所以,你是对的。可读性是这里的主要胜利。有关for each循环或增强for循环的更多信息,请参阅blog entry from oralce

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