7

我同意 foreach 循环减少了打字并且有利于可读性。

稍作备份,我从事低延迟应用程序开发,每秒接收 100 万个数据包进行处理。遍历一百万个数据包并将此信息发送给其侦听器。我正在使用 foreach 循环遍历一组侦听器。

进行分析时,我发现创建了很多Iterator 对象来执行 foreach 循环。将 foreach 循环转换为基于索引的 foreach 我观察到通过减少 no 来创建的对象数量大幅下降。GC 和增加应用程序吞吐量。

编辑:(抱歉混淆,使这个 Q 更清晰)例如,我有听众列表(固定大小),我每秒循环通过这个 forloop 一百万次。foreach 在 java 中是不是有点矫枉过正?

例子:

for(String s:listOfListeners)
{
   // logic
}

相比

for (int i=0;i<listOfListeners.size();i++)
{
   // logic
}

代码的配置屏幕截图

for (int cnt = 0; cnt < 1_000_000; cnt++)
{
    for (String string : list_of_listeners)
    {
         //No Code here 
    }
}

在此处输入图像描述

4

5 回答 5

10

编辑:回答截然不同的问题:

例如,我有侦听器列表(固定大小),并且我每秒循环通过这个 forloop 一百万次。foreach 在 java 中是不是有点矫枉过正?

这取决于 - 您的分析是否真的表明额外的分配很重要?Java 分配器和垃圾收集器每秒可以做很多工作。

换句话说,您的步骤应该是:

  1. 根据您的功能要求设定性能目标
  2. 编写最简单的代码来实现您的功能需求
  3. 衡量该代码是否满足功能要求
  4. 如果没有:
    • 配置文件以找出优化的位置
    • 做出改变
    • 再次运行测试以查看它们是否对您的有意义的指标产生重大影响(分配的对象数量可能不是有意义的指标;您可以处理的侦听器数量可能是)
    • 返回第 3 步。

也许在您的情况下,增强的 for 循环很重要。我不会假设它是 - 我也不会假设每秒创建一百万个对象是重要的。我会在之前和之后测量有意义的指标......并确保在你做任何其他事情之前你有具体的性能目标,否则你将不知道何时停止微优化。


列表的大小约为一百万个对象流入。

所以你正在创建一个迭代器对象,但你正在执行你的循环体一百万次。

进行分析时,我发现创建了很多 Iterator 对象来执行 foreach 循环。

没有?应该只创建一个迭代器对象根据 JLS

增强的 for 语句等效于以下形式的基本 for 语句:

    for (I #i = Expression.iterator(); #i.hasNext(); ) {
        VariableModifiersopt TargetType Identifier =
            (TargetType) #i.next();
        Statement
    }

如您所见,它调用了一次iterator()方法,然后在每次迭代中调用and 。hasNext()next()

您认为额外的对象分配实际上会严重损害您的性能吗?

与性能相比,您对可读性的重视程度如何?我会在任何有助于提高可读性的地方使用增强的 for 循环,直到它被证明是一个性能问题——我个人的经验是,在我编写的任何东西中它都不会显着损害性能。这并不是说对所有应用程序都适用,但默认位置应该是在证明它会显着改善事情之后只使用可读性较差的代码。

于 2013-04-10T18:06:26.493 回答
6

“foreach”循环只创建一个Iterator对象,而第二个循环不创建任何对象。如果您正在执行许多单独的循环,每个循环只执行几次,那么是的,“foreach”可能会不必要地昂贵。否则,这是微优化。

于 2013-04-10T18:07:11.817 回答
1

编辑:自从我写下我的答案以来,这个问题发生了很大的变化,我不确定我现在在回答什么。

如果它是一个链表,查找内容list.get(i)实际上可能会慢很多,因为对于每次查找,它都必须遍历列表,而迭代器会记住位置。

例子:

list.get(0) will get the first element
list.get(1) will first get the first element to find pointer to the next
list.get(2) will first get the first element, then go to the second and then to the third
etc.

所以要做一个完整的循环,你实际上是以这种方式循环元素:

0
0->1
0->1->2
0->1->2->3
etc.
于 2013-04-10T18:13:05.313 回答
0

这种比较是否公平?您正在使用IteratorVs 进行比较get(index)

此外,每个循环只会创建一个额外Iterator的 . 除非由于Iterator某种原因本身效率低下,否则您应该会看到相当的性能。

于 2013-04-10T18:08:18.993 回答
0

我认为你不应该担心这里的有效性。

大部分时间都被您的实际应用程序逻辑所消耗(在这种情况下 - 您在循环中所做的事情)。

所以,我不会担心你在这里为方便而付出的代价。

于 2013-04-10T18:07:53.897 回答