简体   繁体   English

在 for 循环中使用 continue 时性能是否提高

[英]Is performance gained when using continue in a for-loop

continue first?先继续? or Should the code just go ahead?或者代码应该只是 go 前面?

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?那么是什么原因呢?

They are entirely indistinguishable.它们完全无法区分。 Java, like many languages, compiles to bytecode, JVM bytecode in this case. Java 与许多语言一样,编译为字节码,在这种情况下为 JVM 字节码。 And at the bytecode level, things like if statements don't exist.在字节码级别,不存在if语句之类的东西。 They get compiled down to conditional and unconditional jumps to labels.它们被编译成有条件和无条件跳转到标签。 Your first code block will compile to a conditional jump back to the top of the loop.您的第一个代码块将编译为有条件的跳转回循环顶部。 Something like就像是

// Pseudocode
loop:
  _cond = map.containsKey(a)
  jump_if_true loop, _cond
  ...
end:
  jump loop

Your second will compile to something like你的第二个将编译成类似

// Pseudocode
loop:
  _cond = map.containsKey(a)
  jump_if_false end, _cond
  ...
end:
  jump loop

And since end is an unconditional jump to loop , any good optimizer will turn this, immediately, into the former.由于end是一个无条件跳转到loop ,任何好的优化器都会立即将它变成前者。

Just to be sure, I took the two functions you proposed above (replacing ~~~ with a System.out.println ) and compiled them.可以肯定的是,我采用了您在上面提出的两个函数(将~~~替换为System.out.println )并编译了它们。 They produce, verbatim, the same bytecode.它们逐字生成相同的字节码。 After decompiling back to Java, they look entirely identical.反编译回 Java 后,它们看起来完全一样。

So don't micro-optimize.所以不要微优化。 Write your code, and trust the compiler to handle things within its control.编写您的代码,并相信编译器可以处理其控制范围内的事情。 "Should I use continue or if here" is a micro-optimization. “我应该使用continue还是if在这里”是一个微优化。 "Can I turn this triple nested loop into fewer iterations" is probably a good thing to ask up-front. “我可以把这个三重嵌套循环变成更少的迭代吗?” 提前问可能是件好事。

In Java, you can also add labels to the continue statement.在 Java 中,您还可以在continue语句中添加标签。 In fact, continue is actually a syntactic sugar for a goto LABEL statement with LABEL pointing to the top of the most inner loop.实际上, continue实际上是goto LABEL语句的语法糖,其中LABEL指向最内部循环的顶部。

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 files of either one, you'll see the decompiler converting them into your second version:实际上,当反编译它们各自的.class文件时,您会看到反编译器将它们转换为您的第二个版本:

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.除了提到的可读性原因,您的代码嵌套级别少了一层,两者之间没有明显的性能差异。

Its presence 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, because there is nothing that continue offers that you can't do with a Boolean value that changes when a certain condition is not met anymore.相比之下,Scala 语言在标准库中不提供continue语句,因为当某个条件不再满足时,没有什么是您无法使用 Boolean 值更改的continue语句。

Here's how javap sees both your examples.以下是javap如何看待您的两个示例。 I added a println statement instead of ~~~ .我添加了一个 println 语句而不是~~~

Using continue :使用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 :不使用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

Notice in the first version, using continue translates into a goto statement.请注意,在第一个版本中,使用continue会转换为goto语句。

I think this would depend on how much is being done inside the for loop.我认为这将取决于在 for 循环中做了多少。 If its something small that can be executed very quickly, there wouldn't be a huge difference between the two.如果它的小东西可以很快执行,那么两者之间不会有太大的区别。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM