ChatGPT解决这个技术问题 Extra ChatGPT

Java 8 Iterable.forEach() 与 foreach 循环

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

爪哇 8:

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

爪哇 7:

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

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

编辑

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

一个比另一个没有真正的性能优势。第一个选项是受 FP 启发的东西(通常谈论的是更“好”和“清晰”的方式来表达你的代码)。实际上 - 这是相当“风格”的问题。
@Dwb:在这种情况下,这无关紧要。 forEach 没有被定义为并行或类似的东西,所以这两件事在语义上是等价的。当然,可以实现 forEach 的并行版本(标准库中可能已经存在),在这种情况下,lambda 表达式语法将非常有用。
@AardvarkSoup 调用 forEach 的实例是 Stream (lambdadoc.net/api/java/util/stream/Stream.html)。要请求并行执行,可以编写 joins.parallel().forEach(...)
joins.forEach((join) -> mIrc.join(mSession, join)); 真的是 for (String join : joins) { mIrc.join(mSession, join); } 的“简化”吗?您已将标点符号数从 9 个增加到 12 个,以便隐藏 join 的类型。您真正所做的是将两个语句放在一行上。
要考虑的另一点是Java 有限的变量捕获能力。使用 Stream.forEach(),您无法更新局部变量,因为它们的捕获使它们成为最终变量,这意味着您可以在 forEach lambda 中具有有状态的行为(除非您准备好应对一些丑陋的事情,例如使用类状态变量)。

A
Arlo

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

不能使用非最终变量。因此,不能将如下代码转换为 forEach lambda:

对象上一个 = null; for(Object curr : list) { if( prev != null ) foo(prev, curr);上一页 = 当前; }

无法处理已检查的异常。 Lambdas 实际上并没有被禁止抛出检查异常,但是像 Consumer 这样的常见功能接口没有声明任何异常。因此,任何抛出检查异常的代码都必须将它们包装在 try-catch 或 Throwables.propagate() 中。但即使你这样做了,也并不总是清楚抛出的异常会发生什么。它可能会在 forEach() 的某个地方被吞下

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

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

可能会损害性能,因为 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<T>)stream::iterator 将 Streams 转换为 Iterables。更好的替代方法是使用 StreamEx,它修复了许多 Stream API 问题,包括实现 Iterable

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

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

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

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

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

我用来参考的文章:

关于 Java 8 的一切

内外迭代(正如另一张海报所指出的)

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


“为什么要鼓励在表达式的某处隐藏副作用?”是错误的问题。函数式 forEach 用于鼓励函数式风格,即使用表达式没有副作用。如果您遇到 forEach 无法很好地处理您的副作用的情况,您应该感觉到您没有使用正确的工具来完成这项工作。那么简单的答案是,那是因为您的感觉是正确的,所以请留在 for-each 循环中。经典的 for 循环并没有被弃用……
@Holger forEach 如何在没有副作用的情况下使用?
好吧,我不够精确,forEach 是唯一用于副作用的流操作,但它不像您的示例代码那样用于副作用,计数是典型的 reduce 操作。作为重击规则,我建议将每个操作局部变量或将影响控制流(包括异常处理)的操作保留在经典的 for 循环中。关于我认为的原始问题,问题源于有人使用流,其中流源上的简单 for 循环就足够了。使用仅适用于 forEach() 的流
@Holger forEach 适用的副作用示例是什么?
单独处理每个项目并且不尝试改变局部变量的东西。例如,操作项目本身或打印它们,将它们写入/发送到文件、网络流等。如果您质疑这些示例并且没有看到任何应用程序,对我来说没问题;过滤、映射、归约、搜索和(在较小程度上)收集是流的首选操作。 forEach 对我来说似乎很方便与现有 API 链接。当然,对于并行操作。这些不适用于 for 循环。
L
Lii

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

从我的角度来看,主要优点是可以定义在循环中执行的操作,而无需决定它是并行执行还是顺序执行

如果您希望您的循环并行执行,您可以简单地编写 joins.parallelStream().forEach(join -> mIrc.join(mSession, join));您将不得不为线程处理等编写一些额外的代码。

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


他引用的 Oracle 工程师的幻灯片 (blogs.oracle.com/darcy/resource/Devoxx/…) 没有提到这些 lambda 表达式中的并行性。并行性可能发生在 map & 等批量收集方法中。 fold 与 lambda 无关。
在这里,OP 的代码似乎并没有真正受益于自动并行性(特别是因为不能保证会有一个)。我们真的不知道什么是“mIrc”,但“join”看起来并不像可以乱序执行的东西。
Stream#forEachIterable#forEach 不是一回事。 OP 在询问 Iterable#forEach
我使用了 UPDATEX 样式,因为在提出问题和更新答案的时间之间规范发生了变化。如果没有答案的历史,我想会更加混乱。
如果 joins 正在实施 Iterable 而不是 Stream,谁能向我解释为什么这个答案无效?从我读过的几件事来看,如果 joins 实现 Iterable,OP 应该能够执行 joins.stream().forEach((join) -> mIrc.join(mSession, join));joins.parallelStream().forEach((join) -> mIrc.join(mSession, join));
B
Balder

阅读此问题时,您会得到这样的印象,即 Iterable#forEach 与 lambda 表达式的结合是编写传统 for-each 循环的快捷方式/替代品。这是不正确的。来自OP的这段代码:

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

不是作为写作的捷径

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

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

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

它可以替代以下 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);
}

用函数接口替换循环体,如上面的示例所示,使您的代码更加明确:您是说(1)循环体不会影响周围的代码和控制流,以及(2)循环体可以替换为函数的不同实现,而不会影响周围的代码。无法访问外部作用域的非 final 变量并不是函数/lambda 的缺陷,它是一个 功能,它将 Iterable#forEach 的语义与传统 for-each 循环的语义区分开来.一旦习惯了 Iterable#forEach 的语法,它就会使代码更具可读性,因为您会立即获得有关代码的附加信息。

传统的 for-each 循环肯定会在 Java 中保持良好做法(以避免过度使用术语“best practice”)。但这并不意味着,Iterable#forEach 应该被视为不好的做法或不好的风格。使用正确的工具来完成这项工作始终是一种很好的做法,这包括将传统的 for-each 循环与 Iterable#forEach 混合在一起,这是有意义的。

由于此线程中已经讨论了 Iterable#forEach 的缺点,以下是您可能想要使用 Iterable#forEach 的一些原因:

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

使您的代码更具可扩展性和可维护性:使用函数作为循环体允许您用不同的实现替换此函数(请参阅策略模式)。例如,您可以轻松地将 lambda 表达式替换为方法调用,该方法调用可能会被子类覆盖:joins.forEach(getJoinStrategy());然后,您可以使用实现功能接口的枚举提供默认策略。这不仅使您的代码更具可扩展性,而且还提高了可维护性,因为它将循环实现与循环声明分离。

使您的代码更易于调试:将循环实现与声明分开也可以使调试更容易,因为您可以拥有一个专门的调试实现,打印出调试消息,而无需使用 if(DEBUG)System 使您的主代码混乱.out.println()。调试实现例如可以是一个委托,它装饰实际的功能实现。

优化性能关键代码:与此线程中的一些断言相反,Iterable#forEach 确实已经提供了比传统的 for-each 循环更好的性能,至少在使用 ArrayList 并在“-client”模式下运行 Hotspot 时。虽然对于大多数用例来说,这种性能提升很小且可以忽略不计,但在某些情况下,这种额外的性能会产生影响。例如,库维护者肯定会想要评估,是否应该用 Iterable#forEach 替换他们现有的一些循环实现。为了用事实支持这一说法,我用 Caliper 做了一些微基准测试。这是测试代码(需要来自 git 的最新 Caliper):@VmOptions("-server") public class Java8IterationBenchmarks { public static class TestObject { public int result; } public @Param({"100", "10000"}) int elementCount; ArrayList 列表;测试对象[] 数组; @BeforeExperiment public void setup(){ list = new ArrayList<>(elementCount); for (int i = 0; i < elementCount; i++) { list.add(new TestObject()); } 数组 = 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++; } } 返回; } @Benchmark public void timeForEachAnonymousClass(int reps){ for (int i = 0; i < reps; i++) { list.forEach(new Consumer() { @Override public void accept(TestObject t) { t.result++ ; } }); } 返回; } @Benchmark public void timeForEachLambda(int reps){ for (int i = 0; i < reps; i++) { list.forEach(t -> t.result++); } 返回; } @Benchmark public void timeForEachOverArray(int reps){ for (int i = 0; i < reps; i++) { for (TestObject t : array) { t.result++;以下是结果: -client 的结果 -server 的结果 当使用“-client”运行时,Iterable#forEach 优于 ArrayList 上的传统 for 循环,但仍比直接遍历数组慢。使用“-server”运行时,所有方法的性能大致相同。

-client 的结果

-server 的结果

为并行执行提供可选支持:这里已经说过,使用流并行执行 Iterable#forEach 的功能接口的可能性当然是一个重要方面。由于 Collection#parallelStream() 不能保证循环实际上是并行执行的,因此必须将其视为一项可选功能。通过使用 list.parallelStream().forEach(...); 迭代您的列表,您明确地说:此循环支持并行执行,但它不依赖于它。同样,这是一个特点,而不是一个缺陷!通过将并行执行的决策从实际的循环实现中移开,您可以对代码进行可选优化,而不会影响代码本身,这是一件好事。此外,如果默认的并行流实现不能满足您的需求,没有人会阻止您提供自己的实现。例如,您可以根据底层操作系统、集合大小、内核数量和一些首选项设置提供优化的集合: public abstract class MyOptimizedCollection implements Collection{ private enum OperatingSystem{ LINUX, WINDOWS, ANDROID } 私有操作系统 operatingSystem = OperatingSystem.WINDOWS;私有 int numberOfCores = Runtime.getRuntime().availableProcessors();私人集合 委托; @Override public Stream 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{ 返回 this.delegate.stream();案例 LINUX:返回 SomeVerySpecialStreamImplementation.stream(this.delegate.spliterator());案例ANDROID:默认:返回this.delegate.stream(); } } } 这里的好处是,你的循环实现不需要知道或关心这些细节。


你在这个讨论中有一个有趣的观点,并提出了一些观点。我会尝试解决它们。您建议根据有关循环体性质的某些标准在 forEachfor-each 之间切换。遵循这些规则的智慧和纪律是优秀程序员的标志。这样的规则也是他的祸根,因为他周围的人要么不遵守,要么不同意。例如,使用已检查与未检查的异常。这种情况似乎更加微妙。但是,如果主体“不影响环绕代码或流控制”,是不是将其作为一个函数分解出来更好?
感谢 Aleksandr 的详细评论。 But, if the body "does not affect surround code or flow control," isn't factoring it out as a function better?。是的,在我看来,这通常是这种情况——将这些循环分解为函数是很自然的结果。
关于性能问题 - 我想这在很大程度上取决于循环的性质。在我正在进行的一个项目中,我一直在使用类似于 Java 8 之前的 Iterable#forEach 的函数样式循环,只是因为性能提高。有问题的项目有一个类似于游戏循环的主循环,具有未定义数量的嵌套子循环,客户端可以将循环参与者作为函数插入。这样的软件结构极大地受益于Iteable#forEach
在我的评论的最后有一句话:“代码应该用成语说话,使用的成语越少,代码就越清晰,花在决定使用哪个成语上的时间就越少”。当我从 C# 切换到 Java 时,我开始深深体会到这一点。
这是一个糟糕的论点。你可以用它来证明你想要的任何东西:为什么你不应该使用 for 循环,因为 while 循环已经足够好,而且少了一个习惯用法。哎呀,当 goto 可以完成所有这些以及更多操作时,为什么还要使用任何循环、开关或 try/catch 语句。
Z
ZhongYu

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

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

for(int i=0; i<size; i++)
    action.accept(elements[i])

与需要大量脚手架的 for-each 循环相反

Iterator iter = list.iterator();
while(iter.hasNext())
    Object next = iter.next();
    do something with `next`

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

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


为什么迭代器知道最好的方法而迭代器不知道?
没有本质区别,但需要额外的代码来符合迭代器接口,这可能会更昂贵。
@zhong.j.yu 如果你实现了 Collection ,那么无论如何你也实现了 Iterable 。因此,就“添加更多代码来实现缺少的接口方法”而言,没有代码开销,如果这是您的观点的话。正如 mschenk74 所说,似乎没有理由不能调整迭代器以了解如何以最佳方式迭代您的集合。我同意创建迭代器可能会有开销,但说真的,这些东西通常很便宜,你可以说它们的成本为零......
例如迭代树:void forEach(Consumer<T> v){leftTree.forEach(v);v.accept(rootElem);rightTree.forEach(v);},这比外部迭代更优雅,您可以决定如何最好地同步
有趣的是,String.join 方法中的唯一注释(好的,错误的连接)是“不太可能值得 Arrays.stream 开销的元素数量”。所以他们使用豪华的 for 循环。
A
Assaf

TL;DRList.stream().forEach() 是最快的。

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

经典 foreach 的经典 List.forEach() List.stream().forEach() 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
}

此类中的列表将被迭代,并且每次都通过不同的方法将一些 doIt(Integer i) 应用于它的所有成员。在 Main 类中,我运行了 3 次测试方法来预热 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);

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

经典的

for(int i = 0, l = list.size(); i < l; ++i) {
    doIt(list.get(i));
}

执行时间:4.21 毫秒

经典的 foreach

for(Integer i : list) {
    doIt(i);
}

执行时间:5.95 毫秒

List.forEach()

list.forEach((i) -> doIt(i));

执行时间:3.11 毫秒

List.stream().forEach()

list.stream().forEach((i) -> doIt(i));

执行时间:2.79 毫秒

List.parallelStream().forEach

list.parallelStream().forEach((i) -> doIt(i));

执行时间:3.6 毫秒


你怎么得到这些数字?您正在使用哪个基准测试框架?如果您使用 none 而只是简单的 System.out.println 天真地显示此数据,那么所有结果都是无用的。
没有框架。我使用 System.nanoTime()。如果您阅读答案,您会看到它是如何完成的。我不认为这使它毫无用处,因为这是一个 relative 问题。我不在乎某种方法做得如何,我在乎它与其他方法相比做得如何。
这就是良好的微基准测试的目的。既然你没有达到这样的要求,结果是没有用的。
我可以建议您改为了解 JMH,这是用于 Java 本身的内容,并且需要付出很多努力才能获得正确的数字:openjdk.java.net/projects/code-tools/jmh
我同意@LuiggiMendoza。没有办法知道这些结果是一致的还是有效的。天知道我做了多少基准测试,不断报告不同的结果,尤其是取决于迭代顺序、大小等等。
E
Eugene Loy

我觉得我需要扩展我的评论......

关于范式\风格

这可能是最值得注意的方面。 FP 之所以流行,是因为您可以避免副作用。我不会深入研究您可以从中获得什么利弊,因为这与问题无关。

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

最后,我想说的是,您当前所写的内容与品味\风格\范式有关。

关于并行。

从性能的角度来看,使用 Iterable.forEach 而不是 foreach(...) 并没有显着的好处。

根据官方docs on Iterable.forEach

对 Iterable 的内容执行给定的操作,按照迭代时元素发生的顺序,直到所有元素都被处理或操作引发异常。

...即文档非常清楚不会有隐含的并行性。添加一个将违反 LSP。

现在,Java 8 中承诺了“并行集合”,但要与那些你需要我更明确地使用它们并在使用它们时格外小心(例如,参见 mschenk74 的答案)。

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

更新:可能不是那么明显并且一目了然,但还有另一个方面的风格和可读性。

首先 - 普通的旧 forloops 是普通的和旧的。每个人都已经认识他们了。

其次,更重要的是 - 您可能只想将 Iterable.forEach 与单行 lambda 一起使用。如果“body”变得更重——它们往往不那么可读。从这里您有 2 个选项 - 使用内部类 (yuck) 或使用普通的旧 forloop。当人们看到在同一个代码库中以不同的方式/样式完成相同的事情(迭代集合)时,人们经常会感到恼火,这似乎就是这种情况。

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


并行性不需要新的“并行集合”。这仅取决于您是要求连续流(使用collection.stream())还是要求并行流(使用collection.parallelStream())。
@JBNizet 根据文档 Collection.parallelStream() 不保证实现集合将返回并行流。我实际上在想自己,什么时候会发生这种情况,但是,这可能确实取决于收集。
同意。这也取决于收藏。但我的观点是,所有标准集合(ArrayList 等)都可以使用并行 foreach 循环。无需等待“并行集合”。
@JBNizet 同意您的观点,但这并不是我所说的“并行集合”的真正含义。我引用了 Java 8 中添加的 Collection.parallelStream() 作为“并行集合”,类似于 Scala 的概念,它的作用几乎相同。另外,不知道在 JSR 中它是如何被调用的,我看到了几篇论文,它们对这个 Java 8 特性使用了相同的术语。
对于最后一段,您可以使用函数参考:collection.forEach(MyClass::loopBody);
C
Community

最令人愉快的功能 forEach 的限制之一是缺乏检查异常支持。

一个 possible workaround 是用普通的旧 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);
    }

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

Java 8 Lambda function that throws exception?

Java 8: Lambda-Streams, Filter by Method with Exception

How can I throw CHECKED exceptions from inside Java 8 streams?

Java 8: Mandatory checked exceptions handling in lambda expressions. Why mandatory, not optional?


H
Hardik Patel

Java 1.8 forEach 方法优于 1.7 增强 for 循环的优势在于,在编写代码时,您可以只专注于业务逻辑。

forEach 方法将 java.util.function.Consumer 对象作为参数,因此它有助于将我们的业务逻辑放在一个单独的位置,您可以随时重用它。

看看下面的片段,

在这里,我创建了新的类,它将覆盖消费者类的接受类方法,您可以在其中添加额外的功能,不仅仅是迭代..!!!!!! class MyConsumer implements Consumer{ @Override public void accept(Integer o) { System.out.println("在这里您还可以添加您的业务逻辑,将与迭代一起使用,您可以重用它。"+o); } } public class ForEachConsumer { public static void main(String[] args) { // 创建简单的 ArrayList。 ArrayList aList = new ArrayList<>(); for(int i=1;i<=10;i++) aList.add(i); //使用自定义的迭代器调用 forEach。 MyConsumer 消费者 = new MyConsumer(); aList.forEach(消费者); // 为消费者使用 Lambda 表达式。 (函数接口)Consumer lambda = (Integer o) ->{ System.out.println("使用 Lambda 表达式进行迭代并做其他事情(BI).. "+o); }; aList.forEach(lambda); // 使用匿名内部类。 aList.forEach(new Consumer(){ @Override public void accept(Integer o) { System.out.println("使用匿名内部类调用 "+o); } }); } }


对于 for 循环,这也是可能的。