应该使用<或者< =在for循环中

时间:2008-10-08 12:59:57

标签: performance conventions readability

如果你必须循环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”看起来会很奇怪,不会。

39 个答案:

答案 0 :(得分:286)

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

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

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

我希望现实代码中的性能差异微不足道。

答案 1 :(得分:72)

这两个循环迭代7次。我会说一个带有7的人更可读/更清楚,除非你有另一个非常好的理由。

答案 2 :(得分:55)

我记得在我806年大学集会的时候,我的表现更为出色:

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

因为有JNS操作意味着如果没有签名则跳转。使用这意味着在每个循环之后没有内存查找来获得比较值,也没有比较。目前,大多数编译器都会优化寄存器的使用,因此内存不再重要,但您仍然可以获得不需要的比较。

顺便把7或6放在你的循环中引入一个“magic number”。为了更好的可读性,您应该使用具有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&gt; -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&lt; = 10; i ++)”的可读性通常远比丢失一个处理器命令重要。

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

答案 3 :(得分:27)

我总是使用&lt; array.length因为它比&lt; = array.length-1。

更容易阅读

也有&lt; 7并且假设您知道它以0索引开始,那么该数字应该是直观的迭代次数。

答案 4 :(得分:18)

从优化的角度来看,这无关紧要。

从代码风格的角度来看,我更喜欢&lt; 。原因是:

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

更具可读性
for ( int i = 0; i <= array.size() -1; i++ )

也&lt;直接给出迭代次数。

&lt;&lt;是你可以防止很多偶然的错误。

答案 5 :(得分: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,并强制对数组进行每次索引查找的边界检查。

答案 6 :(得分:6)

在性能方面没有任何有效的区别。因此,我会在你正在解决的问题的背景下使用哪个更容易理解。

答案 7 :(得分:5)

我更喜欢:

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

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

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

答案 8 :(得分:4)

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

答案 9 :(得分:4)

Edsger Dijkstra wrote an article在1982年的背后,他认为较低的&lt; = i&lt;上:

  

自然数最小。排除b)和d)中的下界-as - 强制从最小自然数开始的子序列,如下所述进入非自然数的领域。这很难看,所以对于下限,我们更喜欢≤,如a)和c)。现在考虑从最小自然数开始的子序列:当序列收缩到空序列时,包含上限将迫使后者不自然。这很难看,所以对于上限,我们更喜欢&lt;如在a)和d)中。我们得出结论,公约a)是首选。

答案 10 :(得分:4)

我会说使用“&lt; 7”版本,因为这是大多数人会读到的 - 所以如果人们浏览你的代码,他们可能会错误地解释它。

我不担心“&lt;”比“&lt; =”更快,只是为了便于阅读。

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

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”。

答案 11 :(得分:4)

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

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

答案 12 :(得分:4)

在Java 1.5中你可以做到

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

所以对于数组的情况,你不必担心。

答案 13 :(得分: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 ...

答案 14 :(得分:2)

回到大学时,我记得有关这两个操作在CPU上的计算时间相似的事情。当然,我们在集会层面谈论。

然而,如果你正在谈论C#或Java,我真的不认为一个人会比另一个人提速,你获得的几纳秒很可能不值得你引入任何混乱。

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

答案 15 :(得分:2)

稍微说一下,当循环遍历.Net中的数组或其他集合时,我找到了

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

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

答案 16 :(得分:2)

我同意人群说7在这种情况下是有意义的,但是我想补充一点,在6很重要的情况下,假设你要说明你只对第6个索引的对象采取行动,然后&lt; =更好,因为它使6更容易看到。

答案 17 :(得分:2)

这直接属于"Making Wrong Code Look Wrong"类别。

在基于零的索引语言中,例如Java或C#,人们习惯于index < count条件的变体。因此,利用这种事实上的惯例会使一对一的错误变得更加明显。

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

答案 18 :(得分:1)

好问题。我的回答:使用类型A('&lt;')

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

另一个问题是整个构造。 i中出现 3次,因此可能输入错误。 for循环结构说怎么做而不是做什么。我建议采用这个:

BOOST_FOREACH(i, IntegerInterval(0,7))

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

答案 19 :(得分:1)

正如大家所说,即使是数组之外的东西,也习惯使用0索引迭代器。如果所有内容都从0开始并以n-1结束,并且下限始终为<=,而上限始终为<,那么您必须考虑的更少在审查代码时这样做。

答案 20 :(得分:1)

正如人们所观察到的那样,你提到的两种选择中没有一种存在差异。为了证实这一点,我在JavaScript中做了一些简单的基准测试。

您可以看到结果here。不清楚的是,如果我交换第一次和第二次测试的位置,那两次测试的结果交换,这显然是一个内存问题。然而,第三个测试,我反转迭代顺序的测试显然更快。

答案 21 :(得分:1)

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

'&lt;'运算符是标准的,在零基循环中更容易阅读。

使用++ i代替i ++可以提高C ++的性能,但不会提高C#的性能 - 我不了解Java。

答案 22 :(得分:1)

这么多答案......但我相信我还有一些事情需要补充。

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

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

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

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

可读性胜过性能,直到您对其进行分析,因为在此之前您可能不知道编译器或运行时将对代码执行什么操作。

答案 23 :(得分:1)

不要使用幻数。

为什么是7? (或者就此而言是6)。

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

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

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

答案 24 :(得分:1)

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

这几乎肯定比&lt;之间的任何性能差异更重要。和&lt; =。首先瞄准功能性和可读性,然后进行优化。

另一个注意事项是,习惯做++ i而不是i ++会更好,因为fetch和increment需要一个临时的增量而fetch不需要。对于整数,您的编译器可能会优化临时性,但如果您的迭代类型更复杂,则可能无法实现。

答案 25 :(得分:1)

您也可以使用!=代替。这样,如果你在初始化时出错,你将得到一个无限循环,导致错误被提前注意到,并且它导致的任何问题都被限制为陷入循环(而不是在以后遇到问题而不是找到它)。

答案 26 :(得分:1)

我一直偏好:

for ( int count = 7 ; count > 0 ; -- count )

答案 27 :(得分:1)

写作i&lt; 7有很多充分的理由。将循环中的数字7重复7次是好的。性能实际上是相同的。几乎所有人都写了我&lt; 7。如果您是为了可读性而写作,请使用每个人都能立即识别的表格。

答案 28 :(得分: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#背景。

答案 29 :(得分:0)

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

答案 30 :(得分:0)

首先考虑可读性,稍后进行优化(但老实说,我无法想象任何明显的差异)。

请注意0 - &gt; K形式是通过使数组基于0而被携带到C#中的C语言。遵循成语,不要违反最不惊讶的原则。

答案 31 :(得分:0)

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

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

String s = oldString.substring(0,numChars);

等。

我习惯于被排除在上限之外,并且宁愿保持这种方式,除非有充分的理由去改变它。 (示例 - 基于1的索引)

答案 32 :(得分:0)

至少与x86编译器的性能不应该不同。一旦我知道,JL和JLE同时工作。至于可重复性,对于七个元素的数组使用“&lt; 7”是有意义的。

答案 33 :(得分:0)

结果没有意义。

从硬件的角度来看,&lt; = with loopNuumber-1将引入一个额外的计算来执行每次迭代的loopNumber-1。所以我假设&lt;如果时间不超过&lt; =

,将花费更少的时间

答案 34 :(得分:0)

过早优化是万恶之源。除非有充分理由担心<超过<=,否则请考虑可读性。

答案 35 :(得分:0)

没有速度差异,但是&lt;在具有基于0的数组的语言中更可能是正确的。此外,如果您想迭代而不是向上,您可以说:

for (i = 7; --i >= 0; ) ...

答案 36 :(得分:0)

我认为它应该是&lt;。

为什么在少数人会这么做时使用很多单词。一个测试更容易理解,然后两个。通常,更容易进行单元测试和修改。

差异小吗?是。但是为什么在没有保证的情况下增加任何复杂性。

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

答案 37 :(得分:0)

严格来说,从逻辑的角度来看,您必须认为< count<= count更有效率,因为<=也会测试相等性。< / p>

答案 38 :(得分:0)

我认为要么是好的,但是当你选择时,坚持一个或另一个。如果你习惯使用&lt; =,那么尽量不要使用&lt;反之亦然。

我更喜欢&lt; =,但是在你使用从零开始的索引的情况下,我可能会尝试使用&lt;。尽管如此,这都是个人偏好。