【问题标题】:Is the replacement of a foreach loop with Iterable.forEach purely cosmetic?用 Iterable.forEach 替换 foreach 循环纯粹是装饰性的吗?
【发布时间】:2016-10-28 00:32:31
【问题描述】:

我的问题涉及 Java 5 中引入的 Java foreach 循环和 Java 8 中引入的 Iterable.forEach()

假设存在一个带有字符串值列表的程序,该程序的功能是遍历每个单独的字符串并将其用作方法参数。

这样的程序在 Java 5 中可能看起来像这样:

for (String s : stringList) {
    doSomething(s);
}

但是,随着 Java 8 中 Iterable.forEach() 的引入,这样的程序可能如下所示:

stringList.forEach(this::doSomething);

我的问题是,除了保存几行代码之外,还有什么理由使用Iterable.forEach() 而不是 foreach 循环?

【问题讨论】:

  • 如果doSomething 抛出一个检查异常,那么你必须明确地捕捉它...
  • @Tunaki 可能不太清楚,但我的意思是说 doSomething 是一种安全的方法,不会抛出异常。
  • 如果您使用并行流,它会产生更大的影响。

标签: java foreach iterator java-8


【解决方案1】:

提供forEach 使得对元素执行的操作可插入且非常灵活。它可以是直接的 lambda、MethodHandle 甚至是 MethodHanlde,由行为的静态部分和运行时定义的部分动态组合而成。在内部,可以避免创建显式Iterator 对象。否则,您当然可以通过外部循环模拟相同的行为。不过这可能看起来不太好。

【讨论】:

  • 优秀的答案,正是我所希望的。
  • @Deadshot 与 for-each 循环相比,它也有一些明显的限制。您不能在操作之外使用非本地控制流(中断/继续);您无法访问高级可变本地人;即使封闭方法声明了检查异常,您也不能抛出检查异常。一个不能替代另一个。
【解决方案2】:

以下是两者之间需要考虑的 3 个差异:

  • Lambda 无法捕获非最终变量。

  • forEach 不允许 break/continue

  • 检查的异常必须传递的 lambda 中捕获(因为类型是 Consumer<...>)。

List<MyClass> list = ...;

MyClass mc = null;

for(MyClass e : list) {
    if(e.test("bla")) {
        mc = e;
        break;
    }
}               

list.forEach(e -> {
    if(e.test("bla")) {
        mc = e; // Does not compile
        break; // Also, does not compile
    }
});
void method() throws Exception {        
    ...
    for(MyClass e : list) {
        ...
        throw new Exception("Oops");
    }

    list.forEach(e -> {
        ...
        throw new Exception("Oops"); // Error: unhandled exception
    });
}

【讨论】:

  • 你也忘记了:检查的异常(即使声明了封闭的方法来抛出它们)。
  • @BrianGoetz 谢谢,我添加了一个例子:)
【解决方案3】:

基本的好处是内部迭代,如果库决定这样做,您可以使用forEach。当使用增强的 for 循环 (for x : xs) 时,您坚持外部迭代

这个想法是你想要一些函数来获取列表的每个元素(或Iterable)并对其进行处理。非功能性(Java 中的传统)是在您的代码中迭代列表 并执行此操作。这称为外部迭代。这会强制顺序执行。

使用 Java 8,您可以创建和传递 lambda 表达式。因此,您传递接受元素的行为或函数。因此,您放弃了对迭代的控制,让列表实现(可以说是库代码)负责迭代,可能会并行化这些工作。

【讨论】:

  • 有趣。 The contract 确实允许其他订单(如果有记录)。
【解决方案4】:

在新的 Java8 API 中,您可以执行类似的操作

stringList.stream ().filter(...).map(...).forEach(...)

与在 for each for 循环中执行相同的转换大小和过滤相比,这在很多时候会更干净,更不容易出错。

【讨论】:

  • 很好的答案,但不一定适合这个问题。
猜你喜欢
  • 2011-08-14
  • 2013-05-14
  • 2019-04-04
  • 2018-08-20
  • 2013-07-08
  • 2015-01-21
  • 2020-11-24
相关资源
最近更新 更多