Java 8 Iterable.forEach() 与 foreach 循环

问题描述

更好的做法是使用for-each. 除了违反 Keep It Simple, Stupid 原则外,新奇forEach()还至少存在以下不足:

  • 。因此,不能将如下代码转换为 forEach lambda:
Object prev = null;
for(Object curr : list)
{
    if( prev != null )
        foo(prev, curr);
    prev = curr;
}
  • 。Lambdas 实际上并没有被禁止抛出检查异常,但是常见的功能接口,比如Consumer不声明任何。因此,任何引发检查异常的代码都必须将它们包装在try-catchor中Throwables.propagate()。但即使你这样做了,也并不总是清楚抛出的异常会发生什么。它可能会被吞进肚子里的某个地方forEach()

  • returnlambda 中的A等于continuefor-each 中的 a,但没有等价于 a break。做返回值、短路或 设置标志 之类的事情也很困难(如果不违反无非 最终变量 规则,这会稍微缓解一些事情)。“这不仅是一种优化,而且当您考虑到某些序列(例如读取文件中的行)可能有副作用,或者您可能有无限序列时,这一点至关重要。”

  • ,这对于除了需要优化的 0.1% 的代码之外的所有人来说都是一件可怕的、可怕的事情。任何并行代码都必须经过深思熟虑(即使它不使用锁、易失性和传统多线程执行的其他特别讨厌的方面)。任何错误都很难找到。

  • ,因为 JIT 无法将 forEach()+lambda 优化到与普通循环相同的程度,尤其是现在 lambda 是新的。我所说的“优化”并不是指调用 lambdas 的开销(很小),而是指现代 JIT 编译器对运行代码执行的复杂分析和转换。

  • 。流既是自动的(阅读:对您的问题了解不多) ,又 使用专门的(阅读:一般情况下效率低下)并行化策略(叉连接递归分解)。

  • 调试器在显示周围代码中的变量时可能会出现问题,并且诸如单步执行之类的操作可能无法按预期工作。

  • 。实际上,一般来说,复杂的“流利”API 也是如此。复杂的单个语句、大量使用泛型以及缺少中间变量的组合共同产生了令人困惑的错误消息并阻碍了调试。而不是“此方法没有类型 X 的重载”,而是更接近于“您在某处弄乱了类型,但我们不知道在哪里或如何”的错误消息。同样,您不能像将代码分解为多个语句并将中间值保存到变量时那样在调试器中单步执行和检查事物。最后,阅读代码并了解每个执行阶段的类型和行为可能并非易事。

  • 。Java 语言已经有了 for-each 语句。为什么用函数调用代替它?为什么要鼓励在表达式的某处隐藏副作用?为什么要鼓励笨拙的单行字?将常规的 for-each 和新的 forEach 混用是不好的风格。代码应该用惯用语说话(由于重复而容易理解的模式),使用的惯用语越少,代码就越清晰,决定使用哪个惯用语的时间就越少(对于像我这样的完美主义者来说,这是一个很大的时间消耗! )。

如您所见,我不是 forEach() 的忠实拥护者,除非它有意义。

对我来说特别冒犯的是Stream它没有实现Iterable(尽管实际上有方法iterator)并且不能在 for-each 中使用,只能与 forEach() 一起使用。我建议使用(Iterable<T>)stream::iterator. 更好的选择是使用StreamEx,它修复了许多 Stream API 问题,包括实现Iterable.

也就是说,forEach()对以下情况很有用:

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

  • 。如果您的问题与 Streams 和 Spliterators 中内置的性能假设相匹配,那么与使用 ExecutorService 相比,这可以为您节省几行代码

  • ,如同步列表,受益于控制迭代(尽管这在很大程度上是理论上的,除非人们可以提出更多示例)

  • list.forEach (obj::someMethod)。但是,请记住检查异常、更难调试以及减少编写代码时使用的惯用语的数量

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

解决方法

以下哪项是 Java 8 中更好的做法?

爪哇 8:

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

爪哇 7:

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

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

编辑

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