11

我想使用 XOR 运算符交换 java 中两个整数变量的值。

这是我的代码:

int i = 24;
int j = 17;

i ^= j;
j ^= i;
i ^= j;

System.out.println("i : " + i + "\t j : " + j);

它可以正常工作,但以下等效代码不起作用:

int i = 24;
int j = 17;

i ^= j ^= i ^= j;

System.out.println("i : " + i + "\t j : " + j);

输出是这样的:

i : 0    j : 24

第一个变量为零!Java有什么问题?

4

5 回答 5

10

根据Java 规范(Java 7 规范)第 15.26.2 节(第 529 页)。

形式的复合赋值表达式E1 op= E2等价于E1 = (T) ((E1) op (E2)),其中T是 的类型E1,除了E1只计算一次。

根据第 15.7 节评估顺序(第 423 页)(强调我的):

15.7 评估顺序

Java 编程语言保证运算符的操作数看起来是以特定的评估顺序进行评估的,即从左到右。

15.7.1 先计算左手操作数

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

如果运算符是复合赋值运算符(第 15.26.2 节),则左侧操作数的评估包括记住左侧操作数表示的变量以及获取和保存该变量的值以用于隐含的二元运算.

如果二元运算符的左侧操作数的求值突然完成,则右侧操作数的任何部分似乎都没有被求值。

在第 15.26.2 节(第 529 页)中有更详细的描述:

如果左侧操作数表达式不是数组访问表达式,则:

• 首先,评估左侧操作数以产生变量。[修剪]

• 否则,将保存左侧操作数的值,然后计算右侧操作数。[修剪]

• 否则,左侧变量的保存值和右侧操作数的值用于执行复合赋值运算符指示的二元运算。[修剪]

• 否则,二元运算的结果将转换为左侧变量的类型,经过值集转换(第 5.1.13 节)到适当的标准值集(不是扩展指数值集),并且转换结果存储到变量中。

文档中的示例

示例 15.26.2-2。复合赋值左侧的值在评估右侧之前保存

  class Test {
      public static void main(String[] args) {
          int k = 1;
          int[] a = { 1 };
          k += (k = 4) * (k + 2);
          a[0] += (a[0] = 4) * (a[0] + 2);
          System.out.println("k==" + k + " and a[0]==" + a[0]);
      }
  }

所以问题中的表达式被重写并分组为:

i = i ^ (j = j ^ (i = i ^ j));

评估左操作数:

i = 24 ^ (j = 17 ^ (i = 24 ^ 17));
    **

由于 的值没有像预期的那样“更新”,所以当被交换到时i,它会导致 的值i变为 0 。24j

于 2012-07-04T08:40:52.333 回答
1

通过在一个语句中编写全部交换,您依赖于内部i ^= j表达式相对于外部i ^= (...)表达式的副作用。

从 Java 规范(15.26 赋值运算符):

有12个赋值运算符;所有在语法上都是右关联的(它们从右到左分组)。因此,a=b=c 表示 a=(b=c),将 c 的值赋给 b,然后将 b 的值赋给 a。

[...]

赋值运算符:= *= /= %= += -= <<= >>= >>>= &= ^= |= 之一

您可能需要考虑代码的可读性。也许最好将代码放在一个名为 swap() 的方法中,或者通过使用临时变量进行实际交换:

int temp = i;
i = j;
j = temp;
于 2012-07-04T08:10:29.273 回答
1

最左边的i是在更改之前进行评估。

您可以改为:

j ^= (i ^= j);
i ^= j;

这稍微不那么紧凑但有效。

于 2012-07-04T08:43:04.893 回答
0

怎么样:i ^= j ^ (j = j ^ i ^ j);

于 2014-06-05T21:44:49.827 回答
0

与 @nhahtdh 相比,这是一个更短的解决方案。我知道这是一个老问题,但只是想在 Stackoverflow 上记录它:P

i = i ^ j ^ (j = i)

于 2017-03-30T11:02:38.027 回答