gpt4 book ai didi

Java 8 Iterable.forEach() 与 foreach 循环

转载 作者:bug小助手 更新时间:2023-10-28 01:35:41 25 4
gpt4 key购买 nike

以下哪个是 Java 8 中更好的实践?

java 8:

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

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

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

编辑

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

最佳答案

更好的做法是使用 for-each .除了违反保持简单,愚蠢的原则外,新奇的 forEach()至少有以下不足:

  • 不能使用非最终变量 .所以,像下面这样的代码不能变成 forEach lambda:

  • Object prev = null;
    for(Object curr : list)
    {
    if( prev != null )
    foo(prev, curr);
    prev = curr;
    }

  • 无法处理已检查的异常 .实际上并没有禁止 Lambda 抛出已检查的异常,而是禁止诸如 Consumer 之类的常见功能接口(interface)。不要声明任何。因此,任何抛出受检异常的代码都必须将它们包装在 try-catch 中。或 Throwables.propagate() .但即使你这样做,也并不总是清楚抛出的异常会发生什么。它可能会在 forEach() 的某处被吞下。
  • 限流控制 .一个 return在 lambda 中等于 continue在 for-each 中,但没有等效于 break .执行返回值、短路或设置标志等操作也很困难(如果不违反无非最终变量规则,这会稍微缓解一些情况)。 "This is not just an optimization, but critical when you consider that some sequences (like reading the lines in a file) may have side-effects, or you may have an infinite sequence."
  • 可能并行执行 ,这对于除了 0.1% 的代码需要优化之外的所有人来说都是一件可怕的事情。任何并行代码都必须经过深思熟虑(即使它不使用锁、 volatile 和传统多线程执行的其他特别讨厌的方面)。任何错误都很难找到。
  • 可能会影响性能 ,因为 JIT 无法将 forEach()+lambda 优化到与普通循环相同的程度,尤其是现在 lambdas 是新的。我所说的“优化”并不是指调用 lambdas 的开销(很小),而是指现代 JIT 编译器对运行代码执行的复杂分析和转换。
  • 如果您确实需要并行性,那么使用 ExecutorService 可能要快得多,而且难度也不大。 .流都是自动的(阅读:不太了解您的问题)并使用专门的(阅读:一般情况下效率低下)并行化策略( fork-join recursive decomposition )。
  • 使调试更加困惑 ,因为嵌套调用层次结构和,上帝保佑,并行执行。调试器可能会在显示来自周围代码的变量时出现问题,并且诸如单步执行之类的事情可能无法按预期工作。
  • 流通常更难编码、阅读和调试 .实际上,对于复杂的“fluent ” API 来说,这通常是正确的。复杂的单个语句、大量使用泛型和缺乏中间变量的组合会产生令人困惑的错误消息并阻碍调试。而不是“此方法没有类型 X 的重载”,您会收到一条错误消息,更接近于“您在某处弄乱了类型,但我们不知道在哪里或如何弄乱”。同样,您无法像将代码分解为多个语句并将中间值保存到变量中那样轻松地单步调试和检查调试器中的内容。最后,阅读代码并理解每个执行阶段的类型和行为可能并非易事。
  • 像拇指酸痛一样伸出 . Java 语言已经有了 for-each 语句。为什么用函数调用替换它?为什么鼓励在表达式中的某处隐藏副作用?为什么要鼓励笨拙的单线?将常规的 for-each 和新的 forEach 随意混合是不好的风格。代码应该使用成语(由于重复而很快被理解的模式),使用的成语越少,代码越清晰,决定使用哪个成语所花费的时间就越少(对于像我这样的完美主义者来说,这是一个很大的时间消耗! )。

  • 如您所见,我不是 forEach() 的忠实粉丝,除非它有意义。
    对我来说特别冒犯的是 Stream不实现 Iterable (尽管实际上有方法 iterator )并且不能在 for-each 中使用,只能与 forEach() 一起使用。我建议使用 (Iterable<T>)stream::iterator 将 Streams 转换为 Iterables .更好的选择是使用 StreamEx修复了许多 Stream API 问题,包括实现 Iterable .
    也就是说, forEach()对以下情况很有用:
  • 原子地迭代同步列表 .在此之前,用Collections.synchronizedList()生成的列表对于 get 或 set 之类的事情是原子的,但在迭代时不是线程安全的。
  • 并行执行(使用适当的并行流) .如果您的问题符合 Streams 和 Spliterators 中内置的性能假设,那么与使用 ExecutorService 相比,这可以为您节省几行代码。
  • 具体容器哪 ,就像同步列表一样,受益于控制迭代(尽管这主要是理论性的,除非人们能举出更多的例子)
  • 更干净地调用单个函数 通过使用 forEach()和方法引用参数(即 list.forEach (obj::someMethod) )。但是,请记住有关已检查异常、更困难的调试以及减少编写代码时使用的习惯用法数量的要点。

  • 我引用的文章:
  • Everything about Java 8
  • Iteration Inside and Out (正如另一位海报所指出的)

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

    关于Java 8 Iterable.forEach() 与 foreach 循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16635398/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com