Java 一元递增式++;x和x++;在爪哇

Java 一元递增式++;x和x++;在爪哇,java,Java,我目前正在学习用Java编程,我有一个关于标题中列出的一元增量的问题,我在别处找不到。我只是开始玩弄它们,无法决定在for循环中使用哪一个,因为前缀(递增然后求值)和后缀(递增然后求值)之间的行为差异似乎不适用于for语句。所以我两个都试过了,都成功了。这让我很担心,因为我想按照他们应该被使用的方式使用他们 所以我的问题是,它们在增加for循环时真的是可互换的吗,或者在使用一个虎钳和另一个虎钳的过程中,我会遇到一些模糊的问题吗 我决定给它们计时(如下),而且++x肯定比x++运行得快,但我不知道

我目前正在学习用Java编程,我有一个关于标题中列出的一元增量的问题,我在别处找不到。我只是开始玩弄它们,无法决定在for循环中使用哪一个,因为前缀(递增然后求值)和后缀(递增然后求值)之间的行为差异似乎不适用于for语句。所以我两个都试过了,都成功了。这让我很担心,因为我想按照他们应该被使用的方式使用他们

所以我的问题是,它们在增加for循环时真的是可互换的吗,或者在使用一个虎钳和另一个虎钳的过程中,我会遇到一些模糊的问题吗

我决定给它们计时(如下),而且
++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#
in
javap
output)是冗余和不必要的,并对其进行了优化

在编程中,有一个术语叫做过早优化。这是您优化不需要优化的东西的地方,因为它可能会导致性能问题。你真正做的就是让你的代码更复杂。大多数“优秀”程序员(如果存在这样的情况)都会避免这种情况,而是专注于编写可维护的代码。如果可维护代码的性能不好,那么它将得到优化。我记得在这里读过一篇关于3个不同级别的程序员(初学者、中级、高级)以及如何(粗略地)判断您属于哪一类的回复。我看看能不能找到

编辑:快速解释我的答案:
javac
将.java文件编译成字节码。字节码存储在.class文件中。字节码一步一步地告诉JVM如何完成某些事情。例如,
iinc 1,1
告诉它获取变量
1
(在本例中,这是
i
),并将其递增1
iload_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