当前位置: 首页 > 知识库问答 >
问题:

Java8容器“for each”和流“for each”有什么区别[duplicate]

蓟雪峰
2023-03-14

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

共有2个答案

松嘉颖
2023-03-14

请注意,尽管List.foreach看起来类似于List.stream.foreach,但它实际上并没有使用流式传输,因此它将在没有流式传输开销的情况下完成。

为了比较执行代码的复杂性,下面我将展示这两个结构如何工作的简化版本,通过删除验证逻辑来简化。

E、 g.在ArrayList中,这实现为:

public void forEach(Consumer<? super E> action) {
    for (int i = 0; i < size(); i++)
        action.accept(get(i));
}

就是这样。真的就这么简单。

这需要多种方法:

// In Collection
default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

// In ArrayList
public Spliterator<E> spliterator() {
    return new ArrayListSpliterator(0, -1, 0);
}

// In StreamSupport
public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
    return new ReferencePipeline.Head<>(spliterator,
                                        StreamOpFlag.fromCharacteristics(spliterator),
                                        parallel);
}

// In ReferencePipeline
public void forEach(Consumer<? super P_OUT> action) {
    evaluate(ForEachOps.makeRef(action, false));
}

// In ForEachOps
public static <T> TerminalOp<T, Void> makeRef(Consumer<? super T> action,
                                              boolean ordered) {
    return new ForEachOp.OfRef<>(action, ordered);
}

// many method calls eventually leading to

// In ArrayListSpliterator
public void forEachRemaining(Consumer<? super E> action) {
    for (int i = 0; i < size(); i++)
        action.accept(get(i));
}

如您所见,它运行更多的代码,并创建至少 3 个对象。

赖运珧
2023-03-14

for每次(消费者)方法在Iterable接口中声明集合,因此List扩展了该接口for每次(消费者)的默认实现是:

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

如您所见,默认实现只是在 for-each 循环中调用操作。for-each 循环只是语法糖:

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

除了您不能访问for-each循环中的Iterator

< code>Iterable的特定实现可能会改变它实际迭代其元素的方式(它可能使用也可能不使用< code >迭代器),但它几乎总是会归结到某个< code>for或< code>while循环。我说“几乎总是”是因为可能会涉及到某种类型的递归或链接。

现在,使用< code>List.stream()。当您只是尝试按顺序迭代< code>List时,forEach(Consumer)会创建一个不必要的< code>Stream对象。如果您确实需要以管道方式处理一组元素(比如映射、过滤、映射等等),那么您应该只使用流式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.forEachOr的(消费者)

 类似资料:
  • 问题内容: 我了解使用,我可以使用类似链操作或使用并行流。但是,如果我需要执行小的操作(例如,打印列表的元素),它们之间有什么区别? 问题答案: 对于诸如所示的简单情况,它们基本上是相同的。但是,有许多细微的差异可能很重要。 一个问题是订购。使用,顺序不确定。顺序流不太可能发生,但仍可以按任意顺序执行。这确实在并行流中经常发生。相反,如果指定,则总是以的迭代顺序执行。 另一个问题是副作用。必须指定

  • 我理解,使用,我可以使用类似的链操作,或者使用并行流。但是如果我需要执行小操作(例如,打印列表的元素),它们之间有什么区别呢?

  • 这是一个例子:代码A: 另一个代码B可以这样使用: 两者有什么区别,有和没有?

  • 在这篇文章中我将向你演示如何使用Java8中的foreach操作List和Map 1. Foreach操作Map 1.1 正常方式遍历Map Map<String, Integer> items = new HashMap<>(); items.put("A", 10); items.put("B", 20); items.put("C", 30); items.put("D", 40); ite

  • 我正在从文件中读取值并获取列表。如下- 一旦我得到这个

  • 产出: 请提供2个方法将产生不同输出的例子。