128

如果您必须遍历一个循环 7 次,您会使用:

for (int i = 0; i < 7; i++)

或者:

for (int i = 0; i <= 6; i++)

有两个考虑:

  • 表现
  • 可读性

为了性能,我假设使用 Java 或 C#。使用“小于”或“小于或等于”是否重要?如果您对其他语言有见解,请指出是哪一种。

为了便于阅读,我假设基于 0 的数组。

UPD:我提到的基于 0 的数组可能会让人感到困惑。我不是在谈论遍历数组元素。只是一个一般的循环。

下面有一个很好的观点,即使用一个常数来解释这个幻数是什么。所以如果我有“ int NUMBER_OF_THINGS = 7”,那么“ i <= NUMBER_OF_THINGS - 1”会看起来很奇怪,不是吗。

4

39 回答 39

292

第一个是更惯用的。特别是,它表示(在基于 0 的意义上)迭代次数。当使用基于 1 的东西(例如 JDBC、IIRC)时,我可能会想使用 <=。所以:

for (int i=0; i < count; i++) // For 0-based APIs

for (int i=1; i <= count; i++) // For 1-based APIs

我希望在实际代码中性能差异很小。

于 2008-10-08T13:03:44.750 回答
72

这两个循环都迭代了 7 次。我会说带有 7 的那个更易读/更清晰,除非你对另一个有很好的理由。

于 2008-10-08T13:02:32.253 回答
56

我记得我们在大学做 8086 组装的时候,它的性能更高:

for (int i = 6; i > -1; i--)

因为有一个JNS操作意味着如果没有符号则跳转。使用这意味着在每个周期之后没有内存查找来获取比较值,也没有比较。如今,大多数编译器都优化了寄存器的使用,因此内存不再重要,但您仍然可以获得不需要的比较。

顺便说一句,将 7 或 6 放入循环中就是引入了一个“幻数”。为了更好的可读性,您应该使用带有 Intent Revealing Name 的常量。像这样:

const int NUMBER_OF_CARS = 7;
for (int i = 0; i < NUMBER_OF_CARS; i++)

编辑:人们没有得到组装的东西,所以显然需要一个更完整的例子:

如果我们为 (i = 0; i <= 10; i++) 你需要这样做:

    mov esi, 0
loopStartLabel:
                ; Do some stuff
    inc esi
                ; Note cmp command on next line
    cmp esi, 10
    jle exitLoopLabel
    jmp loopStartLabel
exitLoopLabel:

如果我们为 (int i = 10; i > -1; i--) 做,那么你可以摆脱这个:

    mov esi, 10
loopStartLabel:
                ; Do some stuff
    dec esi
                ; Note no cmp command on next line
    jns exitLoopLabel
    jmp loopStartLabel
exitLoopLabel:

我刚刚检查了一下,微软的 C++ 编译器没有做这个优化,但如果你这样做了:

for (int i = 10; i >= 0; i--) 

所以道德是,如果您使用的是 Microsoft C++†,并且升序或降序没有区别,要获得一个快速循环,您应该使用:

for (int i = 10; i >= 0; i--)

而不是其中任何一个:

for (int i = 10; i > -1; i--)
for (int i = 0; i <= 10; i++)

但坦率地说,获得“for (int i = 0; i <= 10; i++)”的可读性通常比错过一个处理器命令重要得多。

† 其他编译器可能会做不同的事情。

于 2008-10-08T13:30:32.823 回答
27

我总是使用 <array.length,因为它比 <= array.length-1 更容易阅读。

也有 < 7 并且假设您知道它以 0 索引开始,因此该数字是迭代次数应该很直观。

于 2008-10-08T13:02:54.250 回答
18

从优化的角度来看,这并不重要。

从代码风格的角度来看,我更喜欢 < 。原因:

for ( int i = 0; i < array.size(); i++ )

可读性比

for ( int i = 0; i <= array.size() -1; i++ )

< 也可以立即为您提供迭代次数。

对 < 的另一个投票是,您可以防止许多意外的错误。

于 2008-10-08T13:04:32.017 回答
10

@Chris,您关于 .Length 在 .NET 中成本高昂的说法实际上是不真实的,在简单类型的情况下恰恰相反。

int len = somearray.Length;
for(i = 0; i < len; i++)
{
  somearray[i].something();
}

实际上比

for(i = 0; i < somearray.Length; i++)
{
  somearray[i].something();
}

后者是运行时优化的案例。由于运行时可以保证 i 是数组的有效索引,因此不会进行边界检查。在前者中,运行时不能保证 i 在循环之前没有被修改,并强制对数组进行边界检查以进行每次索引查找。

于 2008-10-08T16:03:29.130 回答
6

就性能而言,它没有有效的区别。因此,在您正在解决的问题的上下文中,我会使用更容易理解的那个。

于 2008-10-08T13:02:16.797 回答
5

我更喜欢:

for (int i = 0; i < 7; i++)

我认为这更容易转化为“迭代循环 7 次”。

我不确定性能影响 - 我怀疑任何差异都会被编译掉。

于 2008-10-08T13:03:11.083 回答
5

在 C++ 中,我更喜欢 using !=,它适用于所有 STL 容器。并非所有 STL 容器迭代器都具有可比性。

于 2008-10-08T14:56:59.497 回答
4

在 Java 1.5 中你可以这样做

for (int i: myArray) {
    ...
}

所以对于数组的情况你不需要担心。

于 2008-10-08T13:02:52.613 回答
4

我不认为有性能差异。第二种形式肯定更具可读性,您不必在心里减去一个即可找到最后一个迭代次数。

编辑:我看到其他人不同意。就我个人而言,我喜欢查看循环结构中的实际索引号。也许是因为它更让人想起 Perl 的0..6语法,我知道它相当于(0,1,2,3,4,5,6). 如果我看到一个 7,我必须检查它旁边的运算符,看看实际上,索引 7 永远不会到达。

于 2008-10-08T13:03:09.790 回答
4

我会说使用“< 7”版本,因为这是大多数人会阅读的内容 - 所以如果人们略读您的代码,他们可能会错误地解释它。

我不担心“<”是否比“<=”快,只是为了可读性。

如果您确实想提高速度,请考虑以下事项:

for (int i = 0; i < this->GetCount(); i++)
{
  // Do something
}

为了提高性能,您可以将其稍微重新排列为:

const int count = this->GetCount();
for (int i = 0; i < count; ++i)
{
  // Do something
}

请注意从循环中删除 GetCount()(因为将在每个循环中查询)以及“i++”更改为“++i”。

于 2008-10-08T13:09:20.323 回答
4

Edsger Dijkstra在 1982 年写了一篇关于此的文章,他主张 lower <= i < upper:

有一个最小的自然数。排除下界(如 b)和 d)中的下界会强制从最小自然数开始的子序列将下界作为非自然数领域中提到的。这很难看,所以对于下界,我们更喜欢 a) 和 c) 中的 ≤。现在考虑从最小自然数开始的子序列:包含上限将迫使后者在序列缩小到空序列时变得不自然。这很难看,所以对于上限,我们更喜欢 < ,如 a) 和 d)。我们得出结论,约定 a) 是首选。

于 2008-10-08T21:17:15.593 回答
3

首先,不要使用 6 或 7。

更好地使用:

int numberOfDays = 7;
for (int day = 0; day < numberOfDays ; day++){

}

在这种情况下,它比使用更好

for (int day = 0; day <= numberOfDays  - 1; day++){

}

更好(Java / C#):

for(int day = 0; day < dayArray.Length; i++){

}

甚至更好(C#)

foreach (int day in days){// day : days in Java

}

反向循环确实更快,但由于它更难阅读(如果不是你被其他程序员阅读),最好避免使用。尤其是在 C#、Java 中......

于 2008-10-08T14:09:39.233 回答
2

我同意人群说 7 在这种情况下是有意义的,但我要补充一点,在 6 很重要的情况下,说你想明确你只对第 6 个索引的对象起作用,然后<= 更好,因为它使 6 更容易看到。

于 2008-10-08T13:05:02.843 回答
2

回到大学时,我记得这两个操作在 CPU 上的计算时间相似。当然,我们是在装配级别进行讨论。

但是,如果您在谈论 C# 或 Java,我真的不认为其中一个会比另一个提高速度,您获得的几纳秒很可能不值得您引入任何混淆。

就个人而言,我会编写从业务实现角度来看有意义的代码,并确保它易于阅读。

于 2008-10-08T13:05:40.453 回答
2

顺便说一句,在.Net中循环遍历数组或其他集合时,我发现

foreach (string item in myarray)
{
    System.Console.WriteLine(item);
}

比数字 for 循环更具可读性。这当然假设循环代码中没有使用实际的计数器 Int 本身。不知道性能有没有变化。

于 2008-10-08T13:14:41.043 回答
2

这直接属于“使错误的代码看起来错误”的类别。

在从零开始的索引语言中,例如 Java 或 C#,人们习惯于index < count条件的变化。因此,利用这种事实上的约定将使一个错误的错误更加明显。

关于性能:任何值得其内存占用的好的编译器都应该呈现为非问题。

于 2008-10-08T18:36:18.663 回答
1

写 i<7 有很多很好的理由。在循环中迭代 7 次的数字 7 很好。性能实际上是相同的。几乎每个人都写 i<7。如果您是为了可读性而写作,请使用每个人都会立即识别的形式。

于 2008-10-08T13:17:07.937 回答
1

我一直喜欢:

for ( int count = 7 ; count > 0 ; -- count )
于 2008-10-08T13:36:07.147 回答
1

你也可以!=改用。这样,如果您在初始化时出错,您将获得一个无限循环,从而导致更早发现错误,并且它导致的任何问题都仅限于陷入循环中(而不是在很久以后出现问题并且找不到它)。

于 2008-10-08T13:54:29.000 回答
1

当您遍历数组时,养成使用 < 的习惯将使您和读者都保持一致。每个人都有一个标准的约定会更简单。如果您使用的是基于 0 的数组的语言,则 < 是惯例。

这几乎肯定比 < 和 <= 之间的任何性能差异更重要。首先以功能和可读性为目标,然后进行优化。

另一个注意事项是养成使用 ++i 而不是 i++ 的习惯会更好,因为 fetch 和 increment 需要一个临时的和 increment 而 fetch 不需要。对于整数,您的编译器可能会优化暂时离开,但如果您的迭代类型更复杂,它可能无法做到。

于 2008-10-08T14:38:07.397 回答
1

不要使用幻数。

为什么是7?(或6)。

为您要使用的号码使用正确的符号...

在这种情况下,我认为最好使用

for ( int i = 0; i < array.size(); i++ )
于 2008-10-08T14:51:01.007 回答
1

'<' 和 '<=' 运算符的性能成本完全相同。

'<' 运算符是标准的,并且在从零开始的循环中更易于阅读。

使用 ++i 而不是 i++ 可以提​​高 C++ 的性能,但不能提高 C# 的性能——我不了解 Java。

于 2008-10-08T15:03:41.573 回答
1

正如人们所观察到的,您提到的两种选择中的任何一种都没有区别。为了证实这一点,我在 JavaScript 中做了一些简单的基准测试。

你可以在这里看到结果。从中不清楚的是,如果我交换第一次和第二次测试的位置,那么这两个测试的结果交换,这显然是一个内存问题。然而,第三次测试,我颠倒迭代顺序的测试显然更快。

于 2008-10-08T15:47:49.613 回答
1

正如大家所说,习惯上使用 0 索引的迭代器,即使是数组之外的东西。如果一切都开始于0,结束于n-1,下界总是<=,上界总是<,那么在审查代码时,你需要做的思考就会少得多。

于 2008-10-08T16:43:01.653 回答
1

好问题。我的答案:使用A型('<')

  • 您清楚地看到您有多少次迭代 (7)。
  • 两个端点之间的差异是范围的宽度
  • 更少的字符使其更具可读性
  • 您通常拥有元素的总数i < strlen(s)而不是最后一个元素的索引,因此一致性很重要。

另一个问题是整个结构。i出现3 次,所以可能会打错。for-loop 结构说明了如何做而不是做什么。我建议采用这个:

BOOST_FOREACH(i, IntegerInterval(0,7))

这更清楚,编译为exaclty相同的asm指令等。如果你愿意,请向我询问IntegerInterval的代码。

于 2010-01-11T10:43:58.373 回答
1

这么多答案......但我相信我有一些东西要补充。

我的偏好是文字数字清楚地显示“i”将在循环中采用的值。因此,在迭代从零开始的数组的情况下:

for (int i = 0; i <= array.Length - 1; ++i)

如果你只是循环,而不是遍历数组,从 1 计数到 7 非常直观:

for (int i = 1; i <= 7; ++i)

在您对其进行分析之前,可读性胜过性能,因为在此之前您可能不知道编译器或运行时将如何处理您的代码。

于 2010-09-22T00:12:21.897 回答
0

我认为两者都可以,但是当您选择时,请坚持其中一个。如果你习惯使用 <=,那么尽量不要使用 <,反之亦然。

我更喜欢 <=,但是在您使用从零开始的索引的情况下,我可能会尝试使用 <。不过这都是个人喜好。

于 2008-10-08T13:04:28.943 回答
0

严格来说,从逻辑的角度来看,您必须认为这< count会比测试相等性<= count的确切原因更有效。<=

于 2008-10-08T13:11:18.307 回答
0

至少与 x86 编译器的性能应该没有差异。据我所知,JL 和 JLE 同时工作。至于可重复性,对包含七个元素的数组使用“<7”是有意义的。

于 2008-10-08T13:39:56.640 回答
0

我遵循第一种方法,因为该成语经常重复。

for (int index = 0; index < array.length; i++)

字符串 s = oldString.substring(0, numChars);

等等

我已经习惯了被排除在外的上限,除非有充分的理由改变它,否则我更愿意保持这种状态。(示例——基于 1 的索引)

于 2008-10-08T13:47:47.797 回答
0

先追求可读性,然后再优化(尽管老实说,我无法想象有什么明显的区别)。

请注意,0 -> K 形式是通过使数组基于 0 来继承到 C# 中的 C 习语。遵循成语,不要违反最小惊讶原则。

于 2008-10-08T13:51:22.280 回答
0

我更喜欢这个:

for (int i = 0; i < 7; i++)

但是(这只是一个想法),它的可读性可能与数组是基于 0(C#、Java)还是基于 1(VB .NET)有关。我这样说是因为当您使用基于 0 的数组时,您会认为 0-6 会运行 7 次。我认为 0-6 比 1-7 更直观。再说一次,我来自 C++、Java、C# 背景。

于 2008-10-08T15:25:44.603 回答
0

对于某些语言/技术(例如 .NET),使用 .Size 或 .Length 或 size()/length() 是一个坏主意,因为它每次迭代时都会访问该属性,因此将其分配给变量对性能的影响略小。

于 2008-10-08T15:27:47.220 回答
0

结果没有意义。

从硬件的角度来看,带有 loopNumber-1 的 <= 将引入一个额外的计算来执行 loopNumber-1 每次迭代。所以我假设 < 将花费更少的时间,如果不是相同的时间比 <=

于 2008-10-08T18:28:36.153 回答
0

过早的优化是万恶之源。除非有充分的理由担心<over ,否则保持可读性<=

于 2008-10-27T20:41:15.950 回答
0

没有速度差异,但 < 在基于 0 的数组的语言中更可能是正确的。另外,如果你想向下迭代而不是向上迭代,你可以说:

for (i = 7; --i >= 0; ) ...
于 2008-11-04T22:18:49.367 回答
0

我认为它应该是<。

为什么要使用很多词,而少数人会这样做。一项测试比两项更容易理解。因此,以后更容易进行单元测试和修改。

差别很小吗?是的。但是,为什么在没有必要的情况下增加任何复杂性。

最后,当代码开始优化时,您不依赖于任何优化器或解释器的实现。引用爱因斯坦的话,“尽可能简单,但不要简单”。

于 2009-05-13T17:04:11.923 回答