有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

性能是Java foreach循环对重复执行的过度杀伤力

我同意foreach循环减少了输入,并且有利于可读性

通过一点备份,我就可以开发低延迟的应用程序,每秒可以接收到100万个数据包。遍历一百万个数据包,并将这些信息发送给它的侦听器。我使用foreach循环遍历侦听器集

在分析过程中,我发现有很多迭代器对象被创建来执行foreach循环。将foreach循环转换为基于索引的foreach,我发现通过减少GC数量和增加应用程序吞吐量,在那里创建的对象数量大幅下降

编辑:(抱歉,让这个问题更清楚) 例如,我有一个监听器列表(固定大小),我每秒循环这个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 
    }
}

enter image description here


共 (5) 个答案

  1. # 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.
    
  2. # 2 楼答案

    这个比较公平吗?您正在比较使用Iterator与使用get(index)

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

  3. # 3 楼答案

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

    For example i have list of listeners(fixed size) and i loop through this forloop a million times a second. Is foreach an overkill in java?

    这要看情况了——你的分析是否真的表明额外的分配是显著的?Java分配器和垃圾收集器每秒可以做大量的工作

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

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

    也许在你的例子中,增强的for循环是非常重要的。但我不认为是这样,也不认为每秒创造100万个对象是有意义的。我会在…前后测量有意义的指标。。。在做任何其他事情之前,确保你有具体的绩效目标,否则你就不知道什么时候停止微观优化


    Size of list is around a million objects streaming in.

    所以你要创建一个迭代器对象,但是你要执行循环体一百万次

    Doing profiling i figured there are a lot of Iterator objects created to execute foreach loop.

    没有?只应创建一个单个迭代器对象As per the JLS

    The enhanced for statement is equivalent to a basic for statement of the form:

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

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

    你认为额外的对象分配会严重影响你的性能吗

    你认为可读性比性能重要多少?我会在任何有助于可读性的地方使用增强的for循环,直到它被证明是一个性能问题——我个人的经验是,无论我写什么,它都不会显著地影响性能。这并不是说所有应用程序都是这样,但默认的位置应该是在证明可读性较差的代码将显著改善情况后,只使用可读性较差的代码

  4. # 4 楼答案

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

  5. # 5 楼答案

    我认为你不应该担心这里的效率

    大部分时间都被实际的应用程序逻辑所消耗(在本例中,是由您在循环中所做的事情)

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