【问题标题】:Why swapping integer variable by XOR doesn't work in a single line?为什么通过 XOR 交换整数变量在一行中不起作用?
【发布时间】:2012-07-04 16:59:03
【问题描述】:

我想使用 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 出了什么问题?

【问题讨论】:

  • int k = i; i = j; j = k; 有什么问题?
  • Java 很好,你怎么样? :) 为什么需要使用异或?
  • @Hbcdev:我知道有一些简单的交换方法。但我的问题是两个等价的语句没有相同的行为?
  • @giorashc:我也很好;)我只是想知道这些陈述的区别。我认为它们是等效的,但结果不同

标签: java operators


【解决方案1】:

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

From the Java specificiation (15.26 Assignment Operators):

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

[...]

AssignmentOperator:其中之一 = *= /= %= += -= >= >>>= &= ^= |=

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

int temp = i;
i = j;
j = temp;

【讨论】:

  • 但是Java中的副作用评估不是很好定义的吗? (例如,与 C 不同。)
  • Oli:更新了我的答案,我希望它现在能更好地反映我想说的内容。Downvoter:愿意发表评论吗?
  • 我没有投反对票,但我认为您对标准的引用没有解决问题。
【解决方案2】:

根据Java specification(Java 7 规范),Section 15.26.2(第 529 页)。

E1 op= E2 形式的复合赋值表达式等价于E1 = (T) ((E1) op (E2)),其中TE1 的类型,但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的值没有按预期“更新”,当24被交换为j时,会导致i的值变为0。

【讨论】:

    【解决方案3】:

    最左边的i 在更改之前正在被评估。

    你可以这样做:

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

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

    【讨论】:

    • @nhahtdh 的回答比这更彻底。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

      i = i ^ j ^ (j = i)

      【讨论】:

        猜你喜欢
        • 2012-05-19
        • 1970-01-01
        • 2010-09-19
        • 2015-06-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多