【问题标题】:What is the difference between .foreach and .stream().foreach? [duplicate].foreach 和 .stream().foreach 有什么区别? [复制]
【发布时间】:2015-05-19 09:12:28
【问题描述】:

这是一个例子: 代码A:

files.forEach(f -> {
    //TODO
});

另外一个代码 B 可能会以这种方式使用:

files.stream().forEach(f -> { });

两者有什么区别,有stream(),没有stream()

【问题讨论】:

  • 这不太准确。两者在语义上都是等价的,但都不一定有这种实现。
  • 代码 A 等于(文件 f : 文件)
  • 当有一些中间操作(map、skip、concat、substream、distinct、filter、sorted、limit、peek ..)时,应该使用代码 B,否则它是次优的。谢谢大家!

标签: java lambda java-8 java-stream


【解决方案1】:

实际上,它们大多相同,但语义上存在细微差别。

代码 A 由 Iterable.forEach 定义,而代码 B 由 Stream.forEach 定义。 Stream.forEach 的定义允许以任何顺序处理元素——即使是顺序流。 (对于并行流,Stream.forEach 很可能会乱序处理元素。)

Iterable.forEach 从源获取一个迭代器并在其上调用forEachRemaining()。据我所见,集合类上Stream.forEach 的所有当前(JDK 8)实现都将创建一个从源的迭代器之一构建的拆分器,然后在该迭代器上调用forEachRemaining——就像@987654329 @ 做。所以他们做同样的事情,虽然流版本有一些额外的设置开销。

但是,在未来,流实现可能会发生变化,因此不再是这种情况。

(如果要保证处理流元素的顺序,请改用forEachOrdered()。)

【讨论】:

  • 赞成对实施的潜在变化可能会破坏当前合同
  • 令人惊讶的是,Iterable.forEachdefault 实现不会在 Iterator 上调用 forEachRemaining;它使用 for-each 循环进行迭代。默认的Stream.forEach 将在Spliterator 上调用forEachRemaining,这将最终调用Iterator.forEachRemaining,除非Iterable.spliterator() 已被覆盖(大多数JRE 提供的Collections 都会这样做)。因此,即使对于 collection.forEach(…)collection.stream().forEach(…) 基本相同的集合,它们最终也会以不同的代码执行。但对于某些Collections 来说,存在显着差异。
  • VectorCollections.synchronized… 方法返回的集合存在显着差异,因为它们的 forEach 实现将是 synchronized,而调用 stream().forEach(…) 需要手动同步。
【解决方案2】:

在语义方面没有区别,尽管没有stream 的直接实现可能稍微高效一些。

【讨论】:

  • 其实在语义上有的区别,例如订购保证(或在Stream.forEach 案例中不存在)。
【解决方案3】:

流是用于使用操作或迭代的元素序列(即数据结构)。任何集合都可以作为流公开。您对流执行的操作可以是

中间操作(map、skip、concat、substream、distinct、filter、sorted、limit、peek..)产生另一个 java.util.stream.Stream 但中间操作是惰性操作,只有在 终端操作 执行后才会执行。

终端操作(forEach、max、count、matchAny、findFirst、reduce、collect、sum、findAny)产生一个不是流的对象。

基本上它类似于 Unix 中的管道。

【讨论】:

  • 你的回答可能很有见地,但它公然错过了这个问题。
【解决方案4】:

这两种方法都使用终端操作Iterable.forEach,但带有.stream() 的版本也不必要地创建了一个代表列表的Stream 对象。虽然没有区别,但不是最理想的。

【讨论】:

  • 抱歉,这不正确。代码 A 确实是 Iterable.forEach,但代码 B 是 Stream.forEach,它不能保证以任何特定顺序处理元素,即使对于顺序流也是如此。
  • @Stuart:这是一个 API 详细信息,而 StreamSupport 使用底层 Iterable.forEach 用于自 JDK 8 以来的 Iterable 流(不包括数组)。因此,您对执行差异的可能性的担忧是非存在的。我的回答没有任何问题。
  • 关注不可能不存在^^ 不同的是very real,也是。顺便说一句:是什么让Iterable.forEach 终端?
  • @Unihedro 你声称“API 细节”来证明你对实现细节的吸引力很有趣!你的说法是完全错误的; Stream 中的 forEach 方法与 Iterable 中的方法无关。当前版本的 Oracle JDK 中的当前实现恰好使用 Iterable 之一,但这几乎无关紧要,因为您不能指望它保持不变。您只能依赖 API 规范。
  • Stream.forEach 使用当前实现调用Iterable.forEach。这很容易证明:new ArrayList<String>(){ @Override public void forEach(Consumer<? super String> c) { System.out.println("Iterable.forEach"); } }.stream().forEach(x->{});不会打印消息...
猜你喜欢
  • 2013-03-28
  • 2018-11-12
  • 2012-06-11
  • 2016-04-04
  • 2014-06-06
  • 2016-06-11
相关资源
最近更新 更多