morgancodes
morgancodes

Reputation: 25265

In a java enhanced for loop, is it safe to assume the expression to be looped over will be evaluated only once?

In the following:

for (String deviceNetwork : deviceOrganizer.getNetworkTypes(deviceManufacturer)) {
    // do something
}

Is it safe to assume that deviceOrganizer.getNetworkTypes(deviceManufacturer) will be called only once?

Upvotes: 14

Views: 1414

Answers (3)

JRL
JRL

Reputation: 78003

To complement what's been said and verify that the spec is doing what it says, let's look at the generated bytecode for the following class, which implements the old and new style loops to loop over a list returned by a method call, getList():

public class Main {
    static java.util.List getList() { return new java.util.ArrayList(); }
    public static void main(String[] args) {
        for (Object o : getList()) {
            System.out.print(o);
        }
        for (java.util.Iterator itr = getList().iterator(); itr.hasNext(); ) {
            Object o = itr.next(); System.out.print(o);
        }
    }
}

Relevant parts of the output:

   0:   invokestatic    #4; //Method getList
   3:   invokeinterface #5,  1; //InterfaceMethod java/util/List.iterator
   8:   astore_1
   9:   aload_1
   10:  invokeinterface #6,  1; //InterfaceMethod java/util/Iterator.hasNext
   15:  ifeq    35
   18:  aload_1
   19:  invokeinterface #7,  1; //InterfaceMethod java/util/Iterator.next
   24:  astore_2
   25:  getstatic   #8; //Field java/lang/System.out
   28:  aload_2
   29:  invokevirtual   #9; //Method java/io/PrintStream.print
   32:  goto    9
   35:  invokestatic    #4; //Method getList
   38:  invokeinterface #10,  1; //InterfaceMethod java/util/List.iterator
   43:  astore_1
   44:  aload_1
   45:  invokeinterface #6,  1; //InterfaceMethod java/util/Iterator.hasNext
   50:  ifeq    70
   53:  aload_1
   54:  invokeinterface #7,  1; //InterfaceMethod java/util/Iterator.next
   59:  astore_2
   60:  getstatic   #8; //Field java/lang/System.out
   63:  aload_2
   64:  invokevirtual   #9; //Method java/io/PrintStream.print
   67:  goto    44
   70:  return

This shows that the first loop (0 to 32) and the second (35-67) are identical.
The generated bytecode is exactly the same.

Upvotes: 1

OscarRyz
OscarRyz

Reputation: 199234

Yes, give it a try:

public class ForLoop {
    public static void main( String [] args ) {
        for( int i : testData() ){
            System.out.println(i);
        }
    }
    public  static int[] testData() {
        System.out.println("Test data invoked");
        return new int[]{1,2,3,4};
    }
}

Output:

$ java ForLoop
Test data invoked
1
2 
3
4

Upvotes: 9

Jon Skeet
Jon Skeet

Reputation: 1500815

Yes, absolutely.

From section 14.14.2 of the spec:

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
}

(The alternative deals with arrays.)

Note how Expression is only mentioned in the first part of the for loop expression - so it's only evaluated once.

Upvotes: 18

Related Questions