Java 一元递增式++;x和x++;在爪哇
我目前正在学习用Java编程,我有一个关于标题中列出的一元增量的问题,我在别处找不到。我只是开始玩弄它们,无法决定在for循环中使用哪一个,因为前缀(递增然后求值)和后缀(递增然后求值)之间的行为差异似乎不适用于for语句。所以我两个都试过了,都成功了。这让我很担心,因为我想按照他们应该被使用的方式使用他们 所以我的问题是,它们在增加for循环时真的是可互换的吗,或者在使用一个虎钳和另一个虎钳的过程中,我会遇到一些模糊的问题吗 我决定给它们计时(如下),而且Java 一元递增式++;x和x++;在爪哇,java,Java,我目前正在学习用Java编程,我有一个关于标题中列出的一元增量的问题,我在别处找不到。我只是开始玩弄它们,无法决定在for循环中使用哪一个,因为前缀(递增然后求值)和后缀(递增然后求值)之间的行为差异似乎不适用于for语句。所以我两个都试过了,都成功了。这让我很担心,因为我想按照他们应该被使用的方式使用他们 所以我的问题是,它们在增加for循环时真的是可互换的吗,或者在使用一个虎钳和另一个虎钳的过程中,我会遇到一些模糊的问题吗 我决定给它们计时(如下),而且++x肯定比x++运行得快,但我不知道
++x
肯定比x++
运行得快,但我不知道为什么。有人能详细介绍一下吗
谢谢
public class PlusPlus
{
public static void main(String[] args)
{
long startTime1, startTime2, endTime1, endTime2;
final double COUNT = 100000000;
//times x++ incrementing
startTime1 = System.currentTimeMillis();
for(int x = 0; x < COUNT; x++);
endTime1 = System.currentTimeMillis();
System.out.println("x++ loop: " + (endTime1 - startTime1) + " milliseconds");
//times ++x incrementing
startTime2 = System.currentTimeMillis();
for(int x = 0; x < COUNT; ++x);
endTime2 = System.currentTimeMillis();
System.out.println("++x loop: " + (endTime2 - startTime2) + " milliseconds");
}
}
公共类PlusPlus
{
公共静态void main(字符串[]args)
{
长startTime1、startTime2、endTime1、endTime2;
最终重复计数=100000000;
//乘以x++递增
startTime1=System.currentTimeMillis();
对于(int x=0;x
为了返回“旧”值,x++
操作员需要临时复制旧值,从而导致性能略有下降
在您的
for
循环中,您没有使用返回的值,因此++x
更可取。就效率而言,我们是在吹毛求疵。即使您真的尝试优化您的底层代码,x++
vs++x
也不是非常重要。但是,是的,++x
在技术上更快,因为x++
必须存储x的旧值,增加它,然后返回旧值<代码>++x不需要存储它
担心
++x
与x++
的最大原因实际上都是关于返回值。有时候你想要一个比另一个好 ++x:增量x;整体表达式的值是增量后的值
x++:增量x;整体表达式的值是增量之前的值
考虑以下两个部分:
int x = 0;
System.out.println(x++); // Prints 0
// x is now 1
int y = 0;
System.out.println(++y); // Prints 1
// y is now 1
我个人尽量避免将它们用作更大语句中的表达式-我更喜欢独立代码,如下所示:
int x = 0;
System.out.println(x); // Prints 0
x++;
// x is now 1
int y = 0;
y++;
System.out.println(y); // Prints 1
// y is now 1
在这里,我相信每个人都能够计算出打印的内容以及x和y的最终值,而不会太费劲
在某些情况下,在表达式中使用pre/post increment是非常有用的,但首先要考虑可读性。您的测试在了解性能如何工作方面不会产生太多效果。由于JVM的性质,编写性能测试非常困难,以至于编写整个框架都是为了进行基准测试 要真正理解区别是什么,应该使用
javap
反编译代码
我编写了这个类:
public class MyClass {
public static void main(String[] args) {
for (int i = 0; i < 20; i++)
System.out.println(i);
}
}
然后我又做了一次,但这次使用了预增量:
for (int i = 0; i < 20; ++i)
注意到什么了吗?尤其是它们完全一样?
这是因为Java优化了有关预加载和后加载值的额外指令。它认识到它生成的将变量加载到寄存器中的指令(iload#
injavap
output)是冗余和不必要的,并对其进行了优化
在编程中,有一个术语叫做过早优化。这是您优化不需要优化的东西的地方,因为它可能会导致性能问题。你真正做的就是让你的代码更复杂。大多数“优秀”程序员(如果存在这样的情况)都会避免这种情况,而是专注于编写可维护的代码。如果可维护代码的性能不好,那么它将得到优化。我记得在这里读过一篇关于3个不同级别的程序员(初学者、中级、高级)以及如何(粗略地)判断您属于哪一类的回复。我看看能不能找到
编辑:快速解释我的答案:javac
将.java文件编译成字节码。字节码存储在.class文件中。字节码一步一步地告诉JVM如何完成某些事情。例如,iinc 1,1
告诉它获取变量1
(在本例中,这是i
),并将其递增1iload_1
告诉它获取变量1
并加载它,以便可以使用它,通常在操作或方法调用中使用。例如,这:
8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_1
12: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
意思是“获取System.out
并加载它,然后加载变量#1(i
),现在调用方法#3”。当我们调用方法时,加载的第一件事(System.out
)是我们的“目标”,意思是对那个家伙调用方法。加载的所有其他内容都作为参数传递。在本例中,这意味着i
。所以这三行代表了行System.out.println(i)
。字节码只是告诉Java如何真正做到这一点
事实上,在增量前后的反编译代码中都没有iload_1
,这意味着Java对它进行了优化,因为它意识到它实际上不会被使用。在这两种情况下,它都只做了iinc
,而没有iload
,这与此线程中的大多数其他答案完全相反。我不相信此程序实际上会告诉您在一般情况下哪个版本“更快”。可能重复
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: bipush 20
5: if_icmpge 21
8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_1
12: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
15: iinc 1, 1
18: goto 2
21: return
}
8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_1
12: invokevirtual #3; //Method java/io/PrintStream.println:(I)V