views:

221

answers:

5

I have this for-each-loop:

for (Component c : container.getComponents()) {
    // Loop code
}

Is getComponents called on every iteration? Does it make sense to call getComponents before the look and only work on a cached array?

A: 

I would have thought that when compiled, it would be translated to be a "long hand" for loop, as in using for(int i = 0 ....

Isn't the foreach loop just a programming time saver with the actual bytecode translation the same as doing it "long hand"??

In short, getComponent() should only be called once

Richard
No, that's not how the foreach loop works (though it's true that getComponent() will be called only once).
Michael Borgwardt
This didn't deserve the -1 (the actual answer you gave is correct, although the assumption prior to it is wrong), so here's a +1.
Kyle Rozendo
+10  A: 

getComponents will be called once, and will return an Iterator. This iterator will then be called using next() and hasNext() methods.

Update Here's a bit more detail in attempt to out Skeet Jon Skeet on this answer.

The following program shows how the call to iterator is made once, even though there are three items in the collection.

public static void main(String[] args) {
 List<String> list = new ArrayList<String>() {
  public java.util.Iterator<String> iterator() {
   System.out.println("iterator() called");
   return super.iterator();
  };
 };
 list.add("a");
 list.add("b");
 list.add("c");

 for (String s : list) {
  System.out.println(s);
 }
}

Output:

iterator() called
a
b
c

If you run this through a Java decompiler, you'll find the following code:

    String s;
    for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(s))
        s = (String)iterator.next();

Addtionally, since we know from the JLS 14.14.1 that the first section of a for statement is only executed once, we can rest assured that iterator method won't be called multiple times.

brianegge
+15  A: 

No, getComponents() will only be called once.

It's the equivalent of:

for (Iterator<Component> iterator = container.getComponents().getIterator();
     iterator.hasNext(); )
{
    Component c = iterator.next();
    ...
}

From section 14.14.2 of the Java Language Specification:

The enhanced for statement has the form:

EnhancedForStatement:

for ( VariableModifiersopt Type Identifier: Expression) Statement

The Expression must either have type Iterable or else it must be of an array type (§10.1), > or a compile-time error occurs. The scope of a local variable declared in the FormalParameter part of an enhanced for statement (§14.14) is the contained Statement

The meaning of the enhanced for statement is given by translation into a basic for statement.

If the type of Expression is a subtype of Iterable, then let I be the type of the expression Expression.iterator(). The enhanced for statement is equivalent to a basic for statement of the form:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
   VariableModifiersopt Type Identifier = #i.next();
   Statement
}
Jon Skeet
Jon, how does the compiler guarantee that getComponents() has no side effects and does not produce something different in each iteration? (e.g., if another thread changes the component?).
Uri
It doesn't... but the translation above is basically what it's defined to do. I'll try to find the JLS reference.
Jon Skeet
My bad. I misread the source code. I thought the OP was asking whether the control condition in a for loop gets reevaluated. I didn't notice this was a for-each.
Uri
+2  A: 

Is getComponents called on every iteration?

No. Java's foreach loop is based on the Iterable interface, and is equivalent to this code

Iterator<Component> i = container.getComponents().iterator
while(i.hasNext()) {
    Component c = i.next();
    // loop body goes here
}

The only difference is that the Iterator is hidden.

Michael Borgwardt
+3  A: 

Java LanguageSpecification 14.14.2 says:

EnhancedForStatement:
for ( VariableModifiersopt Type Identifier: Expression) Statement

If the type of Expression is a subtype of Iterable, then let I be the type of the expression Expression.iterator(). The enhanced for statement is equivalent to a basic for statement of the form:

for (I #i = Expression.iterator(); #i.hasNext(); ) {

        VariableModifiersopt Type Identifier = #i.next();
   Statement
}

Where #i is a compiler-generated identifier that is distinct from any other identifiers (compiler-generated or otherwise) that are in scope (§6.3) at the point where the enhanced for statement occurs.

So the expression is evaluated only once (during initialization).

Tadeusz Kopec