Java 为什么 x == (x = y) 与 (x = y) == x 不同?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/53749841/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 00:53:56  来源:igfitidea点击:

Why is x == (x = y) not the same as (x = y) == x?

javavariable-assignmentequalityoperator-precedencejls

提问by John McClane

Consider the following example:

考虑以下示例:

class Quirky {
    public static void main(String[] args) {
        int x = 1;
        int y = 3;

        System.out.println(x == (x = y)); // false
        x = 1; // reset
        System.out.println((x = y) == x); // true
     }
}

I'm not sure if there is an item in the Java Language Specification that dictates loading the previous value of a variable for comparison with the right side (x = y) which, by the order implied by brackets, should be calculated first.

我不确定 Java 语言规范中是否有一项规定加载变量的先前值以与右侧 ( x = y)进行比较,按照括号暗示的顺序,应该首先计算右侧 ( )。

Why does the first expression evaluate to false, but the second evaluate to true? I would have expected (x = y)to be evaluated first, and then it would compare xwith itself (3) and return true.

为什么第一个表达式的计算结果为false,而第二个表达式的计算结果为true?我本来希望(x = y)首先被评估,然后它会x与自身进行比较( 3) 并返回true



This question is different from order of evaluation of subexpressions in a Java expressionin that xis definitely not a 'subexpression' here. It needs to be loadedfor the comparison rather than to be 'evaluated'. The question is Java-specific and the expression x == (x = y), unlike far-fetched impractical constructs commonly crafted for tricky interview questions, came from a real project. It was supposed to be a one-line replacement for the compare-and-replace idiom

这个问题是从不同的Java表达式的子表达式的计算顺序x绝对不是一个“子表达式”在这里。它需要加载进行比较而不是“评估”。问题是特定于 Java 的x == (x = y),与通常为棘手的面试问题设计的牵强的不切实际的结构不同,表达式来自真实项目。它应该是比较和替换习语的一行替换

int oldX = x;
x = y;
return oldX == y;

which, being even simpler than x86 CMPXCHG instruction, deserved a shorter expression in Java.

它比 x86 CMPXCHG 指令更简单,值得在 Java 中使用更短的表达式。

采纳答案by Lightness Races in Orbit

which, by the order implied by brackets, should be calculated first

其中,按照括号所暗示的顺序,应该首先计算

No. It is a common misconception that parentheses have any (general) effect on calculation or evaluation order. They only coerce the parts of your expression into a particular tree, binding the right operands to the right operations for the job.

不是。括号对计算或评估顺序有任何(一般)影响是一种常见的误解。它们只将表达式的部分强制转换为特定的树,将正确的操作数绑定到作业的正确操作。

(And, if you don't use them, this information comes from the "precedence" and associativity of the operators, something that's a result of how the language's syntax tree is defined. In fact, this is still exactly how it works when you use parentheses, but we simplify and say that we're not relying on any precedence rules then.)

(而且,如果你不使用它们,这些信息来自运算符的“优先级”和结合性,这是语言语法树定义方式的结果。事实上,这仍然是它在你使用括号,但我们简化并说我们不依赖任何优先级规则。)

Once that's done (i.e. once your code has been parsed into a program) those operands still need to be evaluated, and there are separate rules about how that is done: said rules (as Andrew has shown us) state that the LHS of each operation is evaluated first in Java.

一旦完成(即一旦您的代码已被解析为程序),这些操作数仍然需要评估,并且有关于如何完成的单独规则:所述规则(正如安德鲁向我们展示的那样)声明每个操作的 LHS首先在 Java 中进行评估。

Note that this is not the case in all languages; for example, in C++, unless you're using a short-circuiting operator like &&or ||, the evaluation order of operands is generally unspecified and you shouldn't rely on it either way.

请注意,并非所有语言都如此;例如,在 C++ 中,除非您使用像&&or这样的短路运算符,否则||操作数的计算顺序通常是未指定的,并且您不应该依赖它。

Teachers need to stop explaining operator precedence using misleading phrases like "this makes the addition happen first". Given an expression x * y + zthe proper explanation would be "operator precedence makes the addition happen between x * yand z, rather than between yand z", with no mention of any "order".

教师需要停止使用误导性短语(例如“这使加法首先发生”)来解释运算符优先级。给定一个表达式x * y + z,正确的解释是“运算符优先级使加法发生在x * yand之间z,而不是在yand之间z”,而没有提到任何“顺序”。

回答by Poohl

As LouisWasserman said, the expression is evaluated left to right. And java doesn't care what "evaluate" actually does, it only cares about generating a (non volatile, final) value to work with.

正如 LouisWasserman 所说,表达式从左到右求值。而java并不关心“评估”实际上做了什么,它只关心生成一个(非易失性的,最终的)值来使用。

//the example values
x = 1;
y = 3;

So to calculate the first output of System.out.println(), the following is done:

因此,要计算 的第一个输出System.out.println(),请执行以下操作:

x == (x = y)
1 == (x = y)
1 == (x = 3) //assign 3 to x, returns 3
1 == 3
false

and to calculate the second:

并计算第二个:

(x = y) == x
(x = 3) == x //assign 3 to x, returns 3
3 == x
3 == 3
true

Note that the second value will always evaluate to true, regardless of the initial values of xand y, because you are effectively comparing the assignment of a value to the variable it is assigned to, and a = band bwill, evaluated in that order, always be the same by definition.

请注意,无论xand的初始值如何,第二个值将始终评估为真,y因为您有效地将值的分配与分配给它的变量进行比较,并且a = bb将按该顺序评估,始终相同根据定义。

回答by Andrew Tobilko

==is a binary equality operator.

==是一个二元相等运算符

The left-hand operandof a binary operator appears to be fully evaluated beforeany part of the right-hand operandis evaluated.

Java 11 Specification > Evaluation Order > Evaluate Left-Hand Operand First

二元运算符的左侧操作数似乎在评估右侧操作数的任何部分之前已完全评估。

Java 11 规范 > 求值顺序 > 先求左手操作数

回答by Michael Puckett II

In the first test you're checking does 1 == 3.

在您检查的第一个测试中,是否 1 == 3。

In the second test your checking does 3 == 3.

在第二个测试中,您的检查结果为 3 == 3。

(x = y) assigns the value and that value is tested. In the former example x = 1 first then x is assigned 3. Does 1 == 3?

(x = y) 分配值并测试该值。在前面的例子中,x = 1,然后 x 被赋值为 3。1 == 3 吗?

In the latter, x is assigned 3, and obviously it's still 3. Does 3 == 3?

在后者中,x 被赋值为 3,显然它仍然是 3。3 == 3 吗?

回答by Amit

It is related to operator precedence and how operators are getting evaluated.

它与运算符优先级以及运算符的评估方式有关。

Parentheses '()' has higher precedence and has associativity left to right. Equality '==' come next in this question and has associativity left to right. Assignment '=' come last and has associativity right to left.

括号 '()' 具有更高的优先级并且具有从左到右的结合性。相等 '==' 在这个问题中紧随其后,并且具有从左到右的关联性。赋值 '=' 最后出现并且具有从右到左的关联性。

System use stack to evaluate expression. Expression gets evaluated left to right.

系统使用堆栈来评估表达式。表达式从左到右求值。

Now comes to original question:

现在回到原来的问题:

int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false

First x(1) will be pushed to stack. then inner (x = y) will be evaluated and pushed to stack with value x(3). Now x(1) will be compared against x(3) so result is false.

首先 x(1) 将被压入堆栈。然后将计算内部 (x = y) 并将其推入具有值 x(3) 的堆栈。现在 x(1) 将与 x(3) 进行比较,因此结果为假。

x = 1; // reset
System.out.println((x = y) == x); // true

Here, (x = y) will be evaluated, now x value become 3 and x(3) will be pushed to stack. Now x(3) with changed value after equality will be pushed to stack. Now expression will be evaluated and both will be same so result is true.

在这里, (x = y) 将被评估,现在 x 值变为 3 并且 x(3) 将被压入堆栈。现在 x(3) 在相等后改变值将被压入堆栈。现在将评估表达式并且两者都相同,因此结果为真。

回答by Or10n

It is not the same. The left hand side will always be evaluated before the right hand side, and the brackets don't specify an order of execution, but a grouping of commands.

它不一样。左侧将始终在右侧进行评估,并且括号不指定执行顺序,而是一组命令。

With:

和:

      x == (x = y)

You are basically doing the same as:

你基本上做同样的事情:

      x == y

And xwill have the value of yafter the comparison.

X将价值Ÿ比较之后。

While with:

同时:

      (x = y) == x

You are basically doing the same as:

你基本上做同样的事情:

      x == x

After xtook y's value. And it will always return true.

xy的值之后。它总是会返回true

回答by Ahmad Bedirxan

Basically the first statement x had it's value 1 So Java compares 1 == to new x variable which won't be the same

基本上第一条语句 x 的值是 1 所以 Java 将 1 == 与新的 x 变量进行比较,这将是不同的

In the second one you said x=y which means the value of x changed and so when you call it again it'll be the same value hence why it's true and x ==x

在第二个中,你说 x=y 这意味着 x 的值改变了,所以当你再次调用它时,它会是相同的值,因此为什么它是真的,x ==x

回答by walen

Consider this other, maybe simpler example:

考虑另一个可能更简单的例子:

int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true

Here, the pre-increment operator in ++xmust be applied beforethe comparison is made — just like (x = y)in your example must be calculated beforethe comparison.

在这里,++x必须在进行比较之前应用预增量运算符 in - 就像(x = y)在您的示例中必须在比较之前计算一样

However, expression evaluation still happens left → to → right, so the first comparison is actually 1 == 2while the second is 2 == 2.
The same thing happens in your example.

然而,表达式计算仍然发生在 left → to → right,所以第一个比较实际上1 == 2是第二个是2 == 2
同样的事情发生在你的例子中。

回答by Dervi? Kay?mba??o?lu

Expressions are evaluated from left to right. In this case:

表达式从左到右计算。在这种情况下:

int x = 1;
int y = 3;


x == (x = y)) // false
x ==    t

- left x = 1
- let t = (x = y) => x = 3
- x == (x = y)
  x == t
  1 == 3 //false


(x = y) == x); // true
   t    == x

- left (x = y) => x = 3
           t    =      3 
-  (x = y) == x
-     t    == x
-     3    == 3 //true

回答by Eric Lippert

I'm not sure if there is an item in the Java Language Specification that dictates loading the previous value of a variable...

我不确定 Java 语言规范中是否有一项规定加载变量的先前值...

There is. Next time you are unclear what the specification says, please read the specification and thenask the question if it is unclear.

有。下次你不清楚规范说了什么,请阅读规范,如果不清楚,然后提出问题。

... the right side (x = y)which, by the order implied by brackets, should be calculated first.

... 右侧(x = y),按照括号所暗示的顺序,应该首先计算。

That statement is false. Parentheses do not imply an order of evaluation. In Java, the order of evaluation is left to right, regardless of parentheses. Parentheses determine where the subexpression boundaries are, not the order of evaluation.

这种说法是错误的。括号并不暗示求值顺序。在 Java 中,计算的顺序是从左到右,与括号无关。括号确定子表达式边界的位置,而不是计算顺序。

Why does the first expression evaluate to false, but the second evaluate to true?

为什么第一个表达式的计算结果为 false,而第二个表达式的计算结果为 true?

The rule for the ==operator is: evaluate the left side to produce a value, evaluate the right side to produce a value, compare the values, the comparison is the value of the expression.

==运算符的规则是:计算左边产生一个值,计算右边产生一个值,比较这些值,比较的是表达式的值。

In other words, the meaning of expr1 == expr2is always the same as though you had written temp1 = expr1; temp2 = expr2;and then evaluated temp1 == temp2.

换句话说, 的含义expr1 == expr2始终与您编写temp1 = expr1; temp2 = expr2;然后评估的含义相同temp1 == temp2

The rule for the =operator with a local variable on the left side is: evaluate the left side to produce a variable, evaluate the right side to produce a value, perform the assignment, the result is the value that was assigned.

=左边有局部变量的运算符的规则是:左边求值产生一个变量,右边求值产生一个值,执行赋值,结果就是被赋值的值。

So put it together:

所以把它放在一起:

x == (x = y)

We have a comparison operator. Evaluate the left side to produce a value -- we get the current value of x. Evaluate the right side: that's an assignment so we evaluate the left side to produce a variable -- the variable x-- we evaluate the right side -- the current value of y-- assign it to x, and the result is the assigned value. We then compare the original value of xto the value that was assigned.

我们有一个比较运算符。评估左侧以产生一个值——我们得到 的当前值x。评估右侧:这是一个赋值,所以我们评估左侧以产生一个变量——变量x——我们评估右侧——的当前值y——将其分配给x,结果是分配的值。然后我们将 的原始值与x分配的值进行比较。

You can do (x = y) == xas an exercise. Again, remember, all the rules for evaluating the left side happen before all the rules of evaluating the right side.

你可以做(x = y) == x一个练习。再次记住,评估左侧的所有规则发生在评估右侧的所有规则之前

I would have expected (x = y) to be evaluated first, and then it would compare x with itself (3) and return true.

我本来希望 (x = y) 首先被评估,然后它会将 x 与其自身进行比较 (3) 并返回 true。

Your expectation is based on a set of incorrect beliefs about the rules of Java. Hopefully you now have correct beliefs and will in the future expect true things.

您的期望基于对 Java 规则的一系列错误信念。希望您现在拥有正确的信念,并且将来会期待真实的事物。

This question is different from "order of evaluation of subexpressions in a Java expression"

这个问题不同于“Java 表达式中子表达式的求值顺序”

This statement is false. That question is totally germane.

这个说法是错误的。这个问题完全有关系。

x is definitely not a 'subexpression' here.

x 在这里绝对不是“子表达式”。

This statement is also false. It is a subexpression twicein each example.

这种说法也是错误的。在每个示例中,它是两次子表达式。

It needs to be loaded for the comparison rather than to be 'evaluated'.

它需要加载进行比较而不是“评估”。

I have no idea what this means.

我不知道这是什么意思。

Apparently you still have many false beliefs. My advice is that you read the specification until your false beliefs are replaced by true beliefs.

显然你还有很多错误的信念。我的建议是你阅读规范,直到你的错误信念被真正的信念所取代。

The question is Java-specific and the expression x == (x = y), unlike far-fetched impractical constructs commonly crafted for tricky interview questions, came from a real project.

问题是特定于 Java 的,表达式 x == (x = y) 与通常为棘手的面试问题设计的牵强的不切实际的结构不同,它来自一个真实的项目。

The provenance of the expression is not relevant to the question. The rules for such expressions are clearly described in the specification; read it!

表达的出处与问题无关。规范中清楚地描述了此类表达式的规则;阅读!

It was supposed to be a one-line replacement for the compare-and-replace idiom

它应该是比较和替换习语的一行替换

Since that one-line replacement caused a great deal of confusion in you, the reader of the code, I would suggest that it was a poor choice. Making the code more concise but harder to understand is not a win. It is unlikely to make the code faster.

由于这一行替换在您(代码的读者)中引起了很大的困惑,因此我建议这是一个糟糕的选择。使代码更简洁但更难理解并不是胜利。不太可能使代码更快。

Incidentally, C# has compare and replaceas a library method, which canbe jitted down to a machine instruction. I believe Java does not have such a method, as it cannot be represented in the Java type system.

顺便说一句,C#有比较和替换作为库方法,可以简化为机器指令。我相信 Java 没有这样的方法,因为它不能在 Java 类型系统中表示。