javaarraysperformancedictionary

Is performance gained when using continue in a for-loop


continue first? or Should the code just go ahead?

Example:

for (int a : array) {
    if (map.containsKey(a))
        continue;
    ~~~
}

//or

for (int a : array) {
    if (!map.containsKey(a)) {
        ~~~
    }
}

Which phrase is faster? Or is there no difference in performance? Then what's the reason?


Solution

  • In Java, you can also add labels to the continue statement. In fact, continue is actually a syntactic sugar for a goto LABEL statement with LABEL pointing to the top of the most inner loop.

    for (int a : array) {
        if (map.containsKey(a))
            continue;
        // ... 
    }
    

    Is equivalent to:

    label1:
    for (int a : array) {
        if (map.containsKey(a))
            continue label1;
        // ... 
    }
    

    When compiled, both produce the same bytecode. Actually, when decompiling their respective .class file of either one, you'll see the decompiler converting them into your second version:

    for (int a : array) {
        if (!map.containsKey(a)) {
            // ...
        }
    

    Besides the readability reason mentioned that you get your code one nested level less, there is no noticeable performance difference between the 2.

    The presence of the continue statement in the language syntax is not even necessary. Most probably, it remained for backward compatibility reasons. By contrast, the Scala language doesn't offer a continue statement in the standard library, simply because there is nothing that continue offers that you can't do with a Boolean value that changes when some condition is not met anymore.

    Here's how javap disassembles your examples. I added a println statement instead of ~~~:

    Using continue:

    74: iload         5
    76: iload         4
    78: if_icmpge     118
    81: aload_3
    82: iload         5
    84: iaload
    85: istore        6
    87: aload_1
    88: iload         6
    90: invokestatic  #10         // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    93: invokeinterface #22,  2   // InterfaceMethod java/util/Map.containsKey:(Ljava/lang/Object;)Z
    98: ifeq          104
    101: goto          112
    104: getstatic     #26         // Field java/lang/System.out:Ljava/io/PrintStream;
    107: iload         6
    109: invokevirtual #32         // Method java/io/PrintStream.println:(I)V
    112: iinc          5, 1
    115: goto          74
    

    Without using continue:

      74: iload         5
      76: iload         4
      78: if_icmpge     115
      81: aload_3
      82: iload         5
      84: iaload
      85: istore        6
      87: aload_1
      88: iload         6
      90: invokestatic  #10         // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      93: invokeinterface #22,  2   // InterfaceMethod java/util/Map.containsKey:(Ljava/lang/Object;)Z
      98: ifne          109
     101: getstatic     #26         // Field java/lang/System.out:Ljava/io/PrintStream;
     104: iload         6
     106: invokevirtual #32         // Method java/io/PrintStream.println:(I)V
     109: iinc          5, 1
     112: goto          74
    

    They look almost identical. Notice in the first version, using continue translates into a goto statement.