我正在处理一个leetcode问题[1],注意到一些吸引我眼球的东西。
当我把一行代码写为:
nums[i] = nums[(i++)+count];
我通过了所有的测试,我的答案被接受了。然而,如果我将同一行更改为:
nums[i++] = nums[i+count];
我得到一个ArrayOutOfBounds异常。为什么会这样?
这是完整的代码:
public void moveZeroes(int[] nums) {
int count = 0, i = 0;
while(i+count < nums.length){
if(nums[i+count] == 0)
count++;
else
nums[i] = nums[(i++)+count];
}
for(;i<nums.length; i++)
nums[i] = 0;
}
[1]https://leetcode.com/problems/move-zeroes/
答案在JLS 15.26.1中。基本上,这是情况下的事情顺序
nums[i++] = nums[i+count];
nums
被评估(作为左侧的一部分)- 对CCD_ 2进行评估以找到索引;结果是
i
的原始值,但i
随后递增 - 在这一点上,我们已经评估了要分配给哪个数组元素
- 评估
nums
(作为右侧的一部分) - 对CCD_ 6进行评价;注意,这使用了
i
的已经递增的值,这就是为什么您会得到一个异常 - 我们现在知道要获取哪个数组元素
- 获取该元素的值
- 将值分配给我们之前确定的LHS元素
重要的部分是赋值运算符的左操作数在右操作数上的表达式之前求值。因此,在左操作数中计算数组索引表达式时的副作用会影响右操作数的计算。
在第一个示例中,i
在该迭代中使用完毕后递增。
nums[(i++)+count];
表示在评估CCD_ 10的等价物之后将CCD_。
在第二个示例中,i
在nums[ i +count];
之前递增。
nums[i++]
意味着在评估CCD_ 14的等价物之后将CCD_。
当代码到达nums[ i + count];
时,i
已经递增。在i + count
等于nums.length
的情况下,这会导致ArrayOutOfBounds异常。