Java 8 Iterable.forEach()vs foreach循环

neb*_*kat 436 java for-loop java-8 java-stream

在Java 8中,以下哪项更好?

Java 8:

joins.forEach(join -> mIrc.join(mSession, join));
Run Code Online (Sandbox Code Playgroud)

Java 7:

for (String join : joins) {
    mIrc.join(mSession, join);
}
Run Code Online (Sandbox Code Playgroud)

我有很多可以用lambdas"简化"的for循环,但使用它们真的有什么好处吗?它会改善他们的性能和可读性吗?

编辑

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

Ale*_*sky 477

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

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

    Object prev = null;
    for(Object curr : list)
    {
        if( prev != null )
            foo(prev, curr);
        prev = curr;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 无法处理已检查的异常.实际上并没有禁止Lambdas抛出已检查的异常,但是常见的功能接口Consumer不会声明任何异常.因此,任何抛出已检查异常的代码都必须将它们包装在try-catch或中Throwables.propagate().但即使你这样做,也不总是清楚抛出的异常会发生什么.它可能会吞噬到内心的某个地方forEach()

  • 有限的流量控制.一个return在拉姆达等于continue在换每个,但没有相当于一个break.执行诸如返回值,短路或设置标志之类的事情也是很困难的(如果它不违反no non-final变量规则,这会稍微减轻一些事情)."这不仅仅是一种优化,但是当您考虑某些序列(如读取文件中的行)可能会产生副作用,或者您可能有无限序列时,这一点至关重要."

  • 可能并行执行,这对于除了需要优化的0.1%代码之外的所有人来说都是一个可怕的,可怕的事情.必须考虑任何并行代码(即使它不使用锁,挥发性和传统多线程执行的其他特别令人讨厌的方面).任何错误都很难找到.

  • 可能会损害性能,因为JIT无法将forEach()+ lambda优化为与plain循环相同的程度,特别是现在lambda是新的.通过"优化",我并不是指调用lambdas(这很小)的开销,而是指现代JIT编译器在运行代码时执行的复杂分析和转换.

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

  • 由于嵌套的调用层次结构和上帝禁止的并行执行,使调试更加混乱.调试器可能在显示来自周围代码的变量时出现问题,并且诸如步进之类的事情可能无法按预期工作.

  • 通常,流编码,读取和调试更加困难.实际上,一般而言,复杂的" 流畅 "API 也是如此.复杂的单个语句,大量使用泛型和缺少中间变量的组合共同产生令人困惑的错误消息并阻碍调试.而不是"此方法没有X类型的重载",你得到的错误消息更接近"你搞砸了类型,但我们不知道在哪里或如何." 同样,您不能像在代码被分解为多个语句时那样轻松地在调试器中单步执行并检查事物,并将中间值保存到变量中.最后,阅读代码并理解每个执行阶段的类型和行为可能并非易事.

  • 像拇指一样疼痛.Java语言已经有for-each语句.为什么用函数调用替换它?为什么鼓励在表达式的某处隐藏副作用?为什么鼓励笨重的单行?定期为每一个和每个新的forWach willy-nilly混合是不好的风格.代码应该用成语说话(由于重复而易于理解的模式),使用的习语越少,代码越清晰,决定使用哪种成语的时间就越少(对于像我这样的完美主义者来说,这是一个很大的时间流逝! ).

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

对我来说特别令人反感的是这样的事实:Stream没有实现Iterable(尽管实际上有方法iterator)并且不能在for-each中使用,只能使用forEach().我建议将Streams转换为Iterables (Iterable<T>)stream::iterator.更好的选择是使用StreamEx来修复许多Stream API问题,包括实现Iterable.

也就是说,forEach()对以下内容有用:

  • 以原子方式迭代同步列表.在此之前,生成的列表Collections.synchronizedList()对于诸如get或set之类的东西是原子的,但在迭代时不是线程安全的.

  • 并行执行(使用适当的并行流).如果您的问题与Streams和Spliterators中内置的性能假设相匹配,那么与使用ExecutorService相比,这可以节省几行代码.

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

  • 通过使用forEach()和方法引用参数(即,list.forEach (obj::someMethod))更干净地调用单个函数.但是,请记住检查异常的要点,更难调试,以及减少编写代码时使用的习语数量.

我用来参考的文章:

编辑:看起来一些lambda的原始提议(如http://www.javac.info/closures-v06a.html)解决了我提到的一些问题(当然,添加自己的复杂性).

  • "为什么鼓励在表达中隐藏副作用?"是错误的问题.函数`forEach`是为了鼓励功能风格,即使用没有*副作用的表达式*.如果您遇到这种情况,'forEach`对您的副作用效果不佳,您会觉得您没有使用正确的工具来完成工作.然后简单的答案是,那是因为你的感觉是正确的,所以留在for-each循环中.经典的`for`循环没有被弃用...... (93认同)
  • 可以单独处理每个项目并且不会尝试改变局部变量的东西.例如,操作项目本身或打印它们,将它们写入/发送到文件,网络流等.如果您对这些示例提出疑问并且没有看到任何应用程序,那对我来说没问题.过滤,映射,减少,搜索和(在较小程度上)收集是流的首选操作.forEach看起来像是方便我链接现有的API.当然,对于并行操作.这些不适用于`for`循环. (28认同)
  • @Holger如何使用`forEach`没有副作用? (16认同)
  • 好吧,我不够精确,`forEach`是唯一用于副作用的流操作,但它不是像你的示例代码那样的副作用,计数是典型的`reduce`操作.作为重击规则,我建议保持操作局部变量的每个操作,或者在经典的`for`循环中影响控制流(包括异常处理).关于我认为的原始问题,问题源于这样一个事实,即有人使用一个流,其中一个简单的`for`循环流就足够了.使用`forEach()`只能工作的流 (12认同)
  • @Holger什么是'forEach`适合的副作用的例子? (8认同)
  • 我很困惑*可能并行执行*,这是否意味着它可能会这样做,即使你没有明确告诉它?我重写我的一些循环为'.forEach()',然后读这个答案,不再是不确定.我不希望我的代码在另一个线程意外执行莫名其妙没有我意识到这一点 (6认同)
  • @SimonKuang:我的观点是代码示例不是`forEach`的合适应用程序.适当的功能习语是减少.为'forEach`提出一个不恰当的用例然后声称它是有缺陷的,因为它不能用于解决它是误导性的. (5认同)
  • @ycomp抱歉这个混乱.它不会自行并行执行,除非您从另一个源获取Stream并且无法控制它的创建方式."可能"的意思更像是,"你或其他人可能决定让它并行运行,没有任何理由." (4认同)
  • 另一个奇怪的是,由于Java数组没有实现`java.lang.Iterable`接口,因此您不能将它们与`forEach`一起使用。特别是对于新手来说,很难正确理解为什么List是可迭代的,而不是数组! (3认同)
  • Stream没有实现Iterable的假设解释是Java中的iterables意味着总是可以重用(即可以多次调用iterator()),而在C#中它们被明确允许单独使用.但这似乎不是一个好理由.大多数C#开发人员并不欣赏这一点,并且它不是bug的巨大来源.实际上,Java中已经存在一些单用的Iterables. (2认同)
  • 关于你的非final变量示例,如果你试图使用`forEach`来解决计数问题,那你就错了.计数可以表示为减少,如`myList.stream().reduce(0,(count,str) - > count + 1,(count1,count2) - > count1 + count2);`这是一种风格上的改进吗?换each`?不是这个例子,而是"for-each"对`myList.size()的改进? (2认同)
  • “Stream 没有实现 Iterable” --&gt; 是有充分理由的:一个 `Stream`(如 Iterator)是一个一次性对象,而一个 `Iterable` 必须在每次请求时生成一个 *new* 迭代器。这不应该是 `Stream` 的责任,但是流的 *source*(如 Collection)可以实现它。 (2认同)
  • 说流更难编码/阅读是主观的,也是你的观点。实际上,流畅/声明式 API 的设计目的是更具可读性,并且具有 FP 背景的人会发现它们更易于编码。所以不确定为什么将其添加到答案中。 (2认同)

msc*_*k74 169

当操作可以并行执行时,会考虑优势.(请参阅http://java.dzone.com/articles/devoxx-2012-java-8-lambda-以及 - 有关内部和外部迭代的部分)

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

  • 如果你想让你的循环并行执行,你可以简单地写

     joins.parallelStream().forEach(join -> mIrc.join(mSession, join));
    
    Run Code Online (Sandbox Code Playgroud)

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

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

  • `Stream#forEach`和`Iterable#forEach`不是一回事.OP问的是'Iterable#forEach`. (8认同)
  • 他引用的oracle工程师的幻灯片(https://blogs.oracle.com/darcy/resource/Devoxx/Devoxx2012_ProjectLambda.pdf)没有提到这些lambda表达式中的并行性.并行性可能发生在诸如`map`和`fol​​d`之类的批量收集方法中,这些方法与lambdas并不真正相关. (4认同)
  • 我使用了UPDATEX样式,因为在询问问题的时间和答案更新的时间之间,规范发生了变化.如果没有答案的历史,我会更加困惑. (2认同)

Bal*_*der 104

在阅读这个问题时,可以得到印象,Iterable#forEach结合lambda表达式是编写传统for-each循环的快捷方式/替代.这是不正确的.来自OP的代码:

joins.forEach(join -> mIrc.join(mSession, join));
Run Code Online (Sandbox Code Playgroud)

不是打算作为写作的捷径

for (String join : joins) {
    mIrc.join(mSession, join);
}
Run Code Online (Sandbox Code Playgroud)

并且当然不应该以这种方式使用.相反,它旨在作为写作的快捷方式(尽管它完全相同)

joins.forEach(new Consumer<T>() {
    @Override
    public void accept(T join) {
        mIrc.join(mSession, join);
    }
});
Run Code Online (Sandbox Code Playgroud)

它是以下Java 7代码的替代品:

final Consumer<T> c = new Consumer<T>() {
    @Override
    public void accept(T join) {
        mIrc.join(mSession, join);
    }
};
for (T t : joins) {
    c.accept(t);
}
Run Code Online (Sandbox Code Playgroud)

用上面的例子替换一个带有功能接口的循环体,使你的代码更加明确:你说的是(1)循环体不影响周围的代码和控制流,(2)循环体可以用函数的不同实现替换,而不会影响周围的代码.不能够访问外部范围的非最终变量不是功能/ lambda表达式的逆差,这是一个特征区分的语义Iterable#forEach从传统的for-each循环的语义.一旦习惯了语法Iterable#forEach,它就会使代码更具可读性,因为您可以立即获得有关代码的其他信息.

传统的for-each循环肯定会在Java中保持良好的实践(避免过度使用的术语" 最佳实践 ").但这并不意味着,Iterable#forEach应该被视为不良做法或不良风格.使用正确的工具来完成工作总是很好的做法,这包括将传统的for-loop循环与Iterable#forEach有意义的混合.

由于Iterable#forEach已经在这个线程中讨论了缺点,这里有一些原因,为什么你可能想要使用Iterable#forEach:

  • 使代码更加明确:如上所述,Iterable#forEach 可以使代码在某些情况下更加明确和可读.

  • 使代码更具可扩展性和可维护性:使用函数作为循环体可以使用不同的实现替换此函数(请参阅策略模式).您可以使用方法调用轻松替换lambda表达式,这可能会被子类覆盖:

    joins.forEach(getJoinStrategy());
    
    Run Code Online (Sandbox Code Playgroud)

    然后,您可以使用实现功能接口的枚举来提供默认策略.这不仅使您的代码更具可扩展性,还增加了可维护性,因为它将循环实现与循环声明分离.

  • 为了使代码更易于调试:从声明中分离循环实现也可以使调试更容易,因为您可以使用专门的调试实现,打印出调试消息,而不需要使用主代码混乱if(DEBUG)System.out.println().调试实现可以​​是例如委托,其装饰实际的功能实现.

  • 为了优化性能关键代码:相反,一些在这个线程的断言,Iterable#forEach 已经提供了比更好的性能传统的for-each循环,在使用ArrayList和在"-client"模式下运行热点时最少.虽然这种性能提升很小并且对于大多数用例来说可以忽略不计,但在某些情况下,这种额外的性能可能会有所不同.例如,如果应该替换一些现有的循环实现,那么库维护者肯定会想要评估Iterable#forEach.

    为了用事实支持这个陈述,我已经用Caliper做了一些微基准测试.这是测试代码(需要来自git的最新Caliper):

    @VmOptions("-server")
    public class Java8IterationBenchmarks {
    
        public static class TestObject {
            public int result;
        }
    
        public @Param({"100", "10000"}) int elementCount;
    
        ArrayList<TestObject> list;
        TestObject[] array;
    
        @BeforeExperiment
        public void setup(){
            list = new ArrayList<>(elementCount);
            for (int i = 0; i < elementCount; i++) {
                list.add(new TestObject());
            }
            array = list.toArray(new TestObject[list.size()]);
        }
    
        @Benchmark
        public void timeTraditionalForEach(int reps){
            for (int i = 0; i < reps; i++) {
                for (TestObject t : list) {
                    t.result++;
                }
            }
            return;
        }
    
        @Benchmark
        public void timeForEachAnonymousClass(int reps){
            for (int i = 0; i < reps; i++) {
                list.forEach(new Consumer<TestObject>() {
                    @Override
                    public void accept(TestObject t) {
                        t.result++;
                    }
                });
            }
            return;
        }
    
        @Benchmark
        public void timeForEachLambda(int reps){
            for (int i = 0; i < reps; i++) {
                list.forEach(t -> t.result++);
            }
            return;
        }
    
        @Benchmark
        public void timeForEachOverArray(int reps){
            for (int i = 0; i < reps; i++) {
                for (TestObject t : array) {
                    t.result++;
                }
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    以下是结果:

    当使用"-client"运行时,Iterable#forEach在ArrayList上优于传统的for循环,但仍然比直接迭代数组慢.当使用"-server"运行时,所有方法的性能大致相同.

  • 为并行执行提供可选支持:这里已经说过,Iterable#forEach使用并行执行功能接口的可能性当然是一个重要方面.由于Collection#parallelStream()不保证循环实际上是并行执行的,因此必须将此视为可选功能.通过迭代列表list.parallelStream().forEach(...);,你明确地说:这个循环支持并行执行,但它不依赖于它.再次,这是一个功能,而不是赤字!

    通过将并行执行的决策从实际的循环实现中移开,您允许对代码进行可选的优化,而不会影响代码本身,这是一件好事.此外,如果默认并行流实现不符合您的需求,则没有人阻止您提供自己的实现.您可以提供优化的集合,具体取决于底层操作系统,集合大小,核心数量以及某些首选项设置:

    public abstract class MyOptimizedCollection<E> implements Collection<E>{
        private enum OperatingSystem{
            LINUX, WINDOWS, ANDROID
        }
        private OperatingSystem operatingSystem = OperatingSystem.WINDOWS;
        private int numberOfCores = Runtime.getRuntime().availableProcessors();
        private Collection<E> delegate;
    
        @Override
        public Stream<E> parallelStream() {
            if (!System.getProperty("parallelSupport").equals("true")) {
                return this.delegate.stream();
            }
            switch (operatingSystem) {
                case WINDOWS:
                    if (numberOfCores > 3 && delegate.size() > 10000) {
                        return this.delegate.parallelStream();
                    }else{
                        return this.delegate.stream();
                    }
                case LINUX:
                    return SomeVerySpecialStreamImplementation.stream(this.delegate.spliterator());
                case ANDROID:
                default:
                    return this.delegate.stream();
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这里的好处是,你的循环实现不需要知道或关心这些细节.

  • 在我的批评的最后,有一句话:"代码应该用成语说话,使用的成语越少,代码越清晰,花在决定使用哪种成语上的时间就越少".当我从C#切换到Java时,我开始深深体会到这一点. (7认同)
  • 这是一个不好的论点.你可以使用它来证明你想要的任何东西:为什么你不应该使用for循环,因为while循环足够好,而且不那么成语.哎呀,当goto可以完成所有这些以及更多时,为什么要使用任何循环,切换或try/catch语句. (7认同)
  • 你在这次讨论中有一个有趣的观点,并提出了许多观点.我会尝试解决它们.你建议根据有关循环体性质的一些标准在`forE`和`for-each`之间切换.遵循这些规则的智慧和纪律是优秀程序员的标志.这些规则也是他的祸根,因为他周围的人要么不跟随他们,要么不同意.例如,使用checked vs unchecked Exceptions.这种情况似乎更加微妙.但是,如果身体"不影响环绕声代码或流量控制",那么它是不是更好地将其分解为一种功能? (5认同)
  • 感谢Aleksandr的详细评论.`但是,如果身体"不影响环绕声代码或流量控制",那么"是不是将其作为一种更好的函数来解决?".是的,在我看来通常就是这种情况 - 将这些循环分解为函数是一种自然结果. (3认同)
  • 关于性能问题 - 我想这在很大程度上取决于循环的性质。在我正在进行的一个项目中,我一直在使用类似于 Java 8 之前的“Iterable#forEach”的函数式循环,只是因为性能提高。有问题的项目有一个类似于游戏循环的主循环,带有未定义数量的嵌套子循环,客户端可以在其中插入循环参与者作为函数。这样的软件结构极大地受益于`Iteable#forEach`。 (2认同)
  • 我的全部观点是,人们不应该在一种通常比另一种更可取的意义上比较两种循环方法。两个循环都有其用途,程序员有责任了解两个循环的来龙去脉,以便他可以有效地使用它们。 (2认同)
  • @tapichu实际上,goto引出了更多的"成语" - 非常有创意的解决方案,超越了if,for,while,return,throw.这就是糟糕的原因.for循环限制了while循环可以完成的奇怪事情.等等. (2认同)

Zho*_*gYu 12

forEach()可以实现比for-each循环更快,因为iterable知道迭代其元素的最佳方式,而不是标准迭代器方式.所以区别在于内部循环或外部循环.

例如,ArrayList.forEach(action)可以简单地实现为

for(int i=0; i<size; i++)
    action.accept(elements[i])
Run Code Online (Sandbox Code Playgroud)

而不是需要大量脚手架的for-each循环

Iterator iter = list.iterator();
while(iter.hasNext())
    Object next = iter.next();
    do something with `next`
Run Code Online (Sandbox Code Playgroud)

但是,我们还需要通过使用来计算两个开销成本forEach(),一个是制作lambda对象,另一个是调用lambda方法.它们可能并不重要.

另请参阅http://journal.stuffwithstuff.com/2013/01/13/iteration-inside-and-out/,以比较不同用例的内部/外部迭代.

  • 为什么迭代知道最好的方法,但迭代器不知道? (8认同)
  • 例如迭代一棵树:`void forEach(Consumer <T> v){leftTree.forEach(v); v.accept(rootElem); rightTree.forEach(v);}`,这比外部迭代更优雅,你可以决定如何最好地同步 (4认同)
  • 没有本质区别,但是需要额外的代码来符合迭代器接口,这可能会更昂贵。 (2认同)

Eug*_*Loy 5

我觉得我需要补充一点评论...

关于范式\样式

这可能是最值得注意的方面。FP之所以受欢迎,是因为您可以避免副作用。由于这与问题无关,因此我不会深入研究可从中获得哪些利弊。

但是,我会说使用Iterable.forEach进行的迭代是受FP启发的,而是将更多FP引入Java的结果(具有讽刺意味的是,我想说在纯FP中forEach并没有太多用处,因为除了引入外,它没有任何作用副作用)。

最后,我要说的是,这只是您当前正在编写的“品味\样式\范例”问题。

关于并行性。

从性能的角度来看,与foreach(...)相比,使用Iterable.forEach不会带来任何明显的好处。

根据Iterable.forEach的官方文档

对Iterable的内容执行给定的操作,顺序是元素在迭代时出现,直到所有元素都已处理或该动作引发异常为止。

...即文档非常清楚,不会存在隐式并行性。添加一个将违反LSP。

现在,在Java 8中已承诺有“并行集合”,但是要与我一起使用,您需要更加明确,并格外小心地使用它们(例如,请参阅mschenk74的答案)。

顺便说一句:在这种情况下,将使用Stream.forEach,它不能保证实际工作将并行进行(取决于基础集合)。

更新:可能不那么明显,乍一看有点张扬,但是还有样式和可读性方面的另一个方面。

首先-普通的旧forloop既普通又旧。每个人都已经认识他们。

其次,也是更重要的一点-您可能只想将Iterable.forEach与单层lambda一起使用。如果“身体”变重-他们往往不那么可读。从这里有2个选项-使用内部类(yuck)或使用普通的旧forloop。当人们在相同的代码库中看到相同的事情(集合上的迭代器)时,常常会感到烦恼,这似乎是事实。

同样,这可能是问题,也可能不是问题。取决于从事代码工作的人员。


Ass*_*saf 5

TL; DR:List.stream().forEach()是最快的.

我觉得我应该从基准测试迭代中添加我的结果.我采用了一种非常简单的方法(没有基准测试框架)并对5种不同的方法进行了基准测试:

  1. 经典 for
  2. 经典的foreach
  3. List.forEach()
  4. List.stream().forEach()
  5. List.parallelStream().forEach

测试程序和参数

private List<Integer> list;
private final int size = 1_000_000;

public MyClass(){
    list = new ArrayList<>();
    Random rand = new Random();
    for (int i = 0; i < size; ++i) {
        list.add(rand.nextInt(size * 50));
    }    
}
private void doIt(Integer i) {
    i *= 2; //so it won't get JITed out
}
Run Code Online (Sandbox Code Playgroud)

这个类中的列表应该迭代,并且doIt(Integer i)每次都通过不同的方法应用于所有成员.在Main类中,我运行三次测试方法来预热JVM.然后我运行测试方法1000次,总结每次迭代方法(使用System.nanoTime())所花费的时间.在完成之后,我将该总和除以1000,这就是结果,平均时间.例:

myClass.fored();
myClass.fored();
myClass.fored();
for (int i = 0; i < reps; ++i) {
    begin = System.nanoTime();
    myClass.fored();
    end = System.nanoTime();
    nanoSum += end - begin;
}
System.out.println(nanoSum / reps);
Run Code Online (Sandbox Code Playgroud)

我在i5 4核CPU上运行了这个版本,java版本为1.8.0_05

经典 for

for(int i = 0, l = list.size(); i < l; ++i) {
    doIt(list.get(i));
}
Run Code Online (Sandbox Code Playgroud)

执行时间:4.21毫秒

经典的foreach

for(Integer i : list) {
    doIt(i);
}
Run Code Online (Sandbox Code Playgroud)

执行时间:5.95毫秒

List.forEach()

list.forEach((i) -> doIt(i));
Run Code Online (Sandbox Code Playgroud)

执行时间:3.11毫秒

List.stream().forEach()

list.stream().forEach((i) -> doIt(i));
Run Code Online (Sandbox Code Playgroud)

执行时间:2.79毫秒

List.parallelStream().forEach

list.parallelStream().forEach((i) -> doIt(i));
Run Code Online (Sandbox Code Playgroud)

执行时间:3.6毫秒

  • 这就是良好的微观基准的目的.由于您未满足此类要求,因此结果毫无用处. (30认同)
  • 你怎么得到这些数字?您使用哪个基准框架?如果您使用none并且只是简单的`System.out.println`来天真地显示这些数据,那么所有结果都是无用的. (23认同)
  • 我建议去了解JMH,这就是用于Java本身的东西,并且需要付出很多努力来获得正确的数字:http://openjdk.java.net/projects/code-tools/jmh/ (6认同)
  • 没有框架.我使用`System.nanoTime()`.如果您阅读答案,您将看到它是如何完成的.我不认为这会使它变得无用,因为这是一个_relative_问题.我不关心某种方法的效果如何,我关心它与其他方法相比有多好. (2认同)

Vad*_*zim 5

该功能最大forEach的局限性之一是缺少检查异常支持。

一种可能的解决方法是将终端替换forEach为普通的旧foreach循环:

    Stream<String> stream = Stream.of("", "1", "2", "3").filter(s -> !s.isEmpty());
    Iterable<String> iterable = stream::iterator;
    for (String s : iterable) {
        fileWriter.append(s);
    }
Run Code Online (Sandbox Code Playgroud)

以下是有关lambda和流中检查异常处理的其他解决方法的最受欢迎的问题列表:

抛出异常的Java 8 Lambda函数?

Java 8:Lambda-Streams,按方法进行过滤(异常)

如何从Java 8流中引发CHECKED异常?

Java 8:lambda表达式中的强制检查异常处理。为什么是强制性的而不是可选性的?


归档时间:

查看次数:

345374 次

最近记录:

7 年 前