在java中,我通常会像以下一样进行for循环:
for (int i = 0; i < max; i++) {
something
}
但是最近有一位同事打字了:
for (int i = 0; i < max; ++i) {
something
}
他说后者会更快。这是真的吗?
答案 0 :(得分:56)
不,这不是真的。您可以通过为每个循环计时大量迭代来测量性能,但我相当肯定它们将是相同的。
神话来自C,其中++i
被认为比i++
更快,因为前者可以通过增加然后返回它来实现。后者可以通过将i的值复制到临时变量,递增i,然后返回临时变量来实现。第一个版本不需要制作临时副本,因此很多人认为它更快。但是,如果表达式用作语句,现代C编译器可以优化临时副本,以便在实践中没有区别。
答案 1 :(得分:36)
这个问题需要一些Java字节码。请考虑以下代码:
public class PostPre {
public static void main(String args[]) {
int n = 5;
loop1(n);
loop2(n);
}
public static void loop1(int n) {
for (int i = 0; i < n; i++) {}
}
public static void loop2(int n) {
for (int i = 0; i < n; ++i) {}
}
}
现在编译并反汇编:
$ javac PostPre.java; javap -c PostPre.class
Compiled from "PostPre.java"
public class PostPre {
public PostPre();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_5
1: istore_1
2: iload_1
3: invokestatic #2 // Method loop1:(I)V
6: iload_1
7: invokestatic #3 // Method loop2:(I)V
10: return
public static void loop1(int);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iload_0
4: if_icmpge 13
7: iinc 1, 1
10: goto 2
13: return
public static void loop2(int);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iload_0
4: if_icmpge 13
7: iinc 1, 1
10: goto 2
13: return
}
loop1()
和loop2()
具有相同的字节代码。
答案 2 :(得分:9)
对于任何合理的优化器,它们都是完全相同的。如果您不确定,请查看输出字节码或对其进行分析。
答案 3 :(得分:5)
即使它是,我非常怀疑,你的同事应该花更多的时间来学习如何优化循环表达式。
答案 4 :(得分:2)
在您的环境中尝试此操作
public class IsOptmized {
public static void main(String[] args) {
long foo; //make sure the value of i is used inside the loop
long now = 0;
long prefix = 0;
long postfix = 0;
for (;;) {
foo = 0;
now = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
foo += i;
}
postfix = System.currentTimeMillis() - now;
foo = 0;
now = System.currentTimeMillis();
for (int i = 0; i < 1000000000; ++i) {
foo += i;
}
prefix = System.currentTimeMillis() - now;
System.out.println("i++ " + postfix + " ++i " + prefix + " foo " + foo);
}
}
}
我给了我
i++ 1690 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1600 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1611 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1600 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1692 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000
所以,即使不是那么多,我也认为存在差异
答案 5 :(得分:1)
它不会更快。带有JIT的编译器和JVM将使这些微不足道的差异变得很小。
您可以使用常用的循环优化技术来获得速度优势,例如展开(如果适用)。
答案 6 :(得分:1)
不会有任何差别。
这来自C ++,但即便如此,在这种情况下也没有任何区别。 哪里有区别就是我是一个对象。 i ++必须制作对象的附加副本,因为它必须返回项目的原始未更改值,而++我可以返回更改的对象,以便保存副本。
在使用用户定义对象的c ++中,副本的成本可能很高,所以它绝对值得记住。因此,人们倾向于将它用于int变量,因为它总是同样好......
答案 7 :(得分:1)
使用“javap -c YourClassName”进行反编译并查看结果并从中做出决定。通过这种方式,您可以看到编译器在每种情况下实际执行的操作,而不是您认为的操作。这样你也可以看到为什么一种方式比另一种更快。
答案 8 :(得分:0)
在Java中应该没有区别 - 任何现代编译器 * 都应该在两种情况下生成相同的字节代码(只是iinc
),因为增量表达式的结果不是直接使用
还有第三个选项,仍然是相同的字节代码 * :
for (int i = 0; i < max; i += 1) {
something
}
用Eclipse的编译器测试*
答案 9 :(得分:0)
在Java中没有这样的差异。 Java机器编写代码,无论你编写++ i还是i ++,它都会被转换为字节代码以完全相同的指令集。
但是在C / C ++中存在巨大的差异,如果你没有使用任何优化标志,那么你的循环最多可以慢3次。
使用像-O / -O3这样的优化标志将迫使编译器使输出组装代码更简单(在大多数情况下),因此更快(在大多数情况下)。
答案 10 :(得分:-2)
即使在HotSpot时代,人们会更快,无人问津。 JIT做的第一件事是删除javac所做的所有优化。在那之后,一切都留给JIT来使它快速。