每个`的Java8容器`和每个`的Stream`有什么区别

zzk*_*eee 5 java iteration java-8

我知道我们可以List.foreach 用来遍历,我们也可以List.stream.foreach用来遍历.我不明白哪个在Java8中遍历更好.

Sla*_*law 5

forEach(Consumer)方法在Iterable接口中声明Collection,因此List扩展.默认实现forEach(Consumer)是:

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,默认实现只是调用actionfor-each循环.for-each循环只是语法糖,用于:

for (Iterator<?> iterator = iterable.iterator(); iterator.hasNext(); ) {
    Object element = iterator.next();
    // Do what you need to with element
} 
Run Code Online (Sandbox Code Playgroud)

除非您无法访问Iteratorfor-each循环.

具体实现Iterable可能会改变它实际迭代其元素的方式(它可能会也可能不会使用Iterator),但它几乎总会归结为某些forwhile循环.我说"几乎总是",因为可能涉及某种类型的递归或链接.

现在,当您只是尝试按顺序迭代时,使用List.stream().forEach(Consumer)会创建一个不必要的Stream对象List.如果您确实需要以管道方式处理元素集合(例如映射,过滤,映射更多等等),则应该只使用流API.

因此,对于简单的迭代,在几乎所有情况下,使用的List.stream().forEach(Consumer)性能都不如简单的List.forEach(Consumer)调用.性能提升很可能是微不足道的,但这是一个简单的解决方案,"优化"并不过分; 特别是如果你没有首先犯"错误".如果您不需要它们,请不要创建对象.

简单地使用for-each循环可能更好forEach(Consumer).它比功能更强大的对应物更容易阅读.


编辑

正如Holger的评论中所提到的,它Stream.forEach(Consumer)有一个非常重要的区别Iterable.forEach(Consumer):它不保证元素的遭遇顺序.虽然Iterable.forEach(Consumer)没有为Iterable接口定义迭代顺序,但可以通过扩展接口(例如List)来定义它.当使用Stream,然而,为了不保证不论其来源Stream.

如果您希望在使用Stream必须使用时保证订单Stream.forEachOrdered(Consumer).

  • 这遗漏了最重要的一点,即不同的语义。`Stream.forEach` 将以 *未指定的顺序* 进行迭代,这可能与当前大多数实现中的遇到顺序匹配,但不能保证。另一个是同步集合上的 `forEach` 可能会在集合的锁下执行整个迭代,而 `Stream.forEach` 不会发生这种情况。 (2认同)