循环增量:哪个"更好"?

hel*_*hod 6 java coding-style

当你有一个循环缓冲区表示为一个数组,并且你需要索引环绕时(即,当你达到最高可能的索引并增加它时),它是否"更好":

return (++i == buffer.length) ? 0: i;
Run Code Online (Sandbox Code Playgroud)

要么

return ++i % buffer.length;
Run Code Online (Sandbox Code Playgroud)

使用模运算符有什么缺点吗?它比第一个解决方案的可读性差吗?

编辑:

当然它应该是++ i而不是i ++,改变了.

编辑2:

一个有趣的说明:我在DougBa的ArrayBlockingQueue实现中找到了第一行代码.

pol*_*nts 10

更新:OP在评论中承认它应该是预先增量的.大多数其他答案错过了这个.有证据表明,这种情况的增加导致可怕的可读性:有一个错误,大多数人都看不到它.

最易读的版本如下:

return (i == buffer.length-1) ? 0 : i+1;
Run Code Online (Sandbox Code Playgroud)

使用++会给支票带来不必要的副作用(更不用说我强烈认为你应该使用预增量)

原始代码有什么问题?我们来看看,好吗?

return (i++ == N) ? 0 : i; // OP's original, slightly rewritten
Run Code Online (Sandbox Code Playgroud)

所以我们知道:

  • i是后增量的,所以当i == N-1在return语句之前,这将返回N而不是立即换行到0
    • 这是有意的吗?大多数情况下,意图是N作为独占上限使用
  • 变量名称i通过命名约定建议局部变量,但它真的吗?
    • 由于副作用,需要仔细检查它是否是一个字段

相比下:

return (i == N-1) ? 0 : i+1; // proposed alternative
Run Code Online (Sandbox Code Playgroud)

在这里我们知道:

  • i 未修改,无论是局部变量还是字段都无关紧要
  • 何时i == N-1返回值0,这是更典型的情况

该%方法

或者,您也可以使用以下%版本:

return (i+1) % N;
Run Code Online (Sandbox Code Playgroud)

有什么问题%?好吧,问题是即使大多数人认为它是模运算符,但它不是!它是余数运算符(JLS 15.17.3).很多人经常对此感到困惑.这是一个经典的例子:

boolean isOdd(int n) {
   return (n % 2) == 1; // does this work???
}
Run Code Online (Sandbox Code Playgroud)

那段代码坏了!!! 它返回false所有负值!问题在于-1 % 2 == -1,虽然是数学上的-1 = 1 (mod 2).

%可能很棘手,这就是我推荐三元运算符版本的原因.但最重要的是消除增量的副作用.

也可以看看

  • @Helper:这就是为什么你不应该在这种情况下使用增量; 只使用普通的`+ 1`表达式,它更具可读性.有你的证明:你犯了一个错误,大多数人甚至都看不到它. (2认同)

Dan*_*nas 5

不要让我在两个选项中进行选择,这两个选项都包含后增量(*)和表达式评估.我会说"没有".

(*)更新:后来被修复为preincrement.

  • 然后出乎意料:没有人可以阅读表达式中的post和pre增量,并且它们永远不应该被这样使用,因为为了一个额外的局部变量而不必引入这样的bug. (2认同)