当前位置: 首页 > 知识库问答 >
问题:

Java 8iterable.foreach()vs foreach循环

郗亦
2023-03-14

以下哪一项在Java 8中的练习更好?

Java 8:

joins.forEach(join -> mIrc.join(mSession, join));

Java 7:

for (String join : joins) {
    mIrc.join(mSession, join);
}

我有很多for循环可以用lambdas“简化”,但是使用它们真的有什么好处吗?这会提高它们的性能和可读性吗?

编辑

我还将这个问题扩展到更长的方法。我知道您不能从lambda返回或中断父函数,在比较它们时也应该考虑到这一点,但是还有什么要考虑的吗?

共有2个答案

柴衡
2023-03-14

当操作可以并行执行时,这一优点就被考虑在内了。(参见http://java.dzone.com/articles/devoxx-2012-java-8-lambda-and-关于内部和外部迭代的部分)

>

  • 从我的观点来看,主要的优点是可以定义要在循环内完成的实现,而不必决定它是并行执行还是顺序执行

    如果希望循环并行执行,您可以简单地编写

     joins.parallelStream().forEach(join -> mIrc.join(mSession, join));
    

    您将不得不为线程处理等编写一些额外的代码。

    注意:对于我的回答,我假设joins实现了java.util.Stream接口。如果joins仅实现java.util.iterable接口,则这不再是真的。

  • 宣滨海
    2023-03-14

    更好的实践是使用for-each。新的foreach()除了违反“保持简单、愚蠢”的原则外,至少还存在以下不足:

    • 不能使用非final变量。因此,不能将以下代码转换为forEach lambda:
    Object prev = null;
    for(Object curr : list)
    {
        if( prev != null )
            foo(prev, curr);
        prev = curr;
    }
    

    >

  • 无法处理检查的异常。lambda实际上并不禁止抛出检查异常,但是像consumer这样的通用函数接口不声明任何异常。因此,任何抛出已检查异常的代码都必须将它们包装在try-catchthrowables.propagate()中。但即使这样做,也不总是很清楚抛出的异常会发生什么。它可能会在foreach()的内脏中被吞噬

    有限流量控制。lambda中的return等于for-each中的continue,但没有与break等价物。它也很难执行返回值、短路或设置标志之类的操作(如果不是违反无非最终变量规则的话,这会稍微减轻一些事情)。“这不仅仅是一种优化,当你考虑到某些序列(比如读取文件中的行)可能有副作用,或者你可能有一个无限的序列时,这是非常关键的。”

    可能并行执行,这是一个可怕的,可怕的事情,除了0.1%的代码需要优化。任何并行代码都必须经过深思熟虑(即使它不使用锁、挥发物和传统多线程执行的其他特别讨厌的方面)。任何bug都很难找到。

    可能会损害性能,因为JIT不能像普通循环那样优化forEach()+lambda,特别是现在lambda是新的。我所说的“优化”并不是指调用lambdas的开销(它很小),而是指现代JIT编译器对运行代码执行的复杂分析和转换。

    如果您确实需要并行性,那么使用ExecutorService可能要快得多,也不会困难得多。流既是自动的(读:不太了解您的问题),又使用了专门的(读:一般情况下效率低)并行化策略(fork-join递归分解)。

    由于嵌套的调用层次结构和(但愿不允许)并行执行,使得调试更加混乱。调试器可能会在显示周围代码中的变量时出现问题,并且像逐步执行这样的事情可能不会像预期的那样工作。

    流通常更难编码、读取和调试。实际上,对于复杂的“流利的”API来说,通常都是如此。复杂的单语句、泛型的大量使用和中间变量的缺乏的组合会产生令人迷惑的错误消息并使调试受挫。而不是“此方法没有类型X的重载”,您得到的错误消息更接近于“您在某个地方搞乱了类型,但我们不知道在哪里或如何搞乱了类型”。类似地,您不能像将代码分解为多个语句,并且将中间值保存到变量中时那样轻松地在调试器中逐级检查。最后,阅读代码并理解执行的每个阶段的类型和行为可能不是微不足道的。

    像拇指痛一样伸出来。Java语言已经有for-each语句。为什么要用函数调用替换它?为什么鼓励在表情中隐藏副作用呢?为什么要鼓励笨拙的俏皮话呢?将常规的for-each和新的forEach随便混在一起是不好的风格。代码应该使用习语(因为它们的重复,所以很快就能理解的模式),使用的习语越少,代码就越清晰,花在决定使用哪个习语上的时间就越少(对于像我这样的完美主义者来说,这是一个很大的时间消耗!)。

    正如您所看到的,我不是forEach()的忠实粉丝,除非它有意义。

    令我特别反感的是,stream没有实现iterable(尽管它实际上有iterator)方法,并且不能在for-each中使用,只能使用forEach()。我建议使用(iterable )stream::iterator 将流强制转换为可迭代文件。更好的替代方法是使用StreamEx,它修复了许多流API问题,包括实现iterable

    话虽如此,foreach()对以下内容很有用:

    >

  • 原子迭代同步列表。在此之前,使用collections.synchronizedList()生成的列表相对于get或set是原子的,但在迭代时不是线程安全的。

    并行执行(使用适当的并行流)。如果您的问题与Streams和Spliterator中内置的性能假设相匹配,则与使用ExecutorService相比,这将为您节省几行代码。

    特定的容器,如同步列表,从控制迭代中受益(尽管这主要是理论上的,除非人们能够提出更多的例子)

    通过使用foreach()和方法引用参数(即list.foreach(obj::someMethod))更清晰地调用单个函数。但是,请记住关于检查的异常、更困难的调试以及减少您在编写代码时使用的习惯用法的数量的要点。

    我参考的文章:

    • 关于Java的一切8
    • 里里外外迭代(如另一位发帖人所指出)

    Edit:看起来Lambda的一些原始建议(如http://www.javac.info/closures-v06a.html Google Cache)解决了我提到的一些问题(当然,同时增加了它们自身的复杂性)。

  •  类似资料:
    • 您好,我对jquery没有什么问题。首先,我有: 大众BORA 1.9TDI 1990 1995 奥迪A3 2.0TFSI 2006 2008 但我想实现: VW BORA 1.9TDI 1990 VW BORA 1.9TDI 1991 VW BORA 1.9TDI 1992 VW BORA 1.9TDI 1993 VW BORA 1.9TDI 1994 VW BORA 1.9TDI 1995 A

    • 问题内容: 我第一次不了解PHP。我一直在脚本中使用for循环,while循环,foreach循环。我想知道 哪一个性能更好? 选择循环的标准是什么? 当我们在另一个循环中循环时应该使用哪个? 我一直想知道要使用哪个循环的代码。 很明显,我可以使用while编写上面的代码。希望有人能帮助我找出哪个循环更适合使用。 问题答案: 哪一个性能更好? 没关系 选择循环的标准是什么? 如果只需要遍历对象或数

    • 通常你想在一个任务中干很多事,比如创建一群用户,安装很多包,或者重复一个轮询步骤直到收到某个特定结果. 本章将对在playbook中如何使用循环做全面的介绍. Topics 循环 为了保持简洁,重复的任务可以用以下简写的方式: - name: add several users user: name={{ item }} state=present groups=wheel with_it

    • 循环其实不足为奇。跟其它程序设计语言一样,bash中的循环也是只要控制条件为真就一直迭代执行的代码块。 Bash中有四种循环:for,while,until和select。 for循环 for与它在C语言中的姊妹非常像。看起来是这样: for arg in elem1 elem2 ... elemN do # 语句 done 在每次循环的过程中,arg依次被赋值为从elem1到elemN。这些

    • 尽管已经支持JavaScript原生代码,Jade还是支持了一些特殊的标签,它们可以让模板更加易于理解,其中之一就是each, 这种形式: each VAL[, KEY] in OBJ 一个遍历数组的例子 : - var items = ["one", "two", "three"] each item in items li= item 渲染为: <li>one</li> <li>two</

    • 可能存在需要多次执行代码块的情况。 通常,语句按顺序执行:首先执行函数中的第一个语句,然后执行第二个语句,依此类推。 编程语言提供各种控制结构,允许更复杂的执行路径。 循环语句允许我们多次执行语句或语句组,以下是大多数编程语言中循环语句的一般形式 - Perl编程语言提供以下类型的循环来处理循环要求。 Sr.No. 循环类型和描述 1 while 循环 在给定条件为真时重复语句或语句组。 它在执行

    • 在本章中,我们将了解Loops如何在LESS中工作。 循环语句允许我们多次执行语句或语句组。 当递归mixin与Guard Expressions和Pattern Matching结合使用时,可以创建各种迭代/循环结构。 例子 (Example) 以下示例演示了在LESS文件中使用循环 - loop_example.htm <!doctype html> <head> <link

    • 当您需要多次执行代码块时,可能会出现这种情况。 通常,语句按顺序执行:首先执行函数中的第一个语句,然后执行第二个语句,依此类推。 编程语言提供各种控制结构,允许更复杂的执行路径。 循环语句允许我们多次执行语句或语句组,以下是大多数编程语言中循环语句的一般形式 - Objective-C编程语言提供以下类型的循环来处理循环需求。 单击以下链接以查看其详细信息。 Sr.No. 循环类型和描述 1 wh