forEach(Consumer) 方法在 Iterable 接口中声明,Collection 和 List 扩展了该接口。 forEach(Consumer) 的默认实现是:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
如您所见,默认实现只是在 for-each 循环中调用 action。 for-each 循环只是语法糖:
for (Iterator<?> iterator = iterable.iterator(); iterator.hasNext(); ) {
Object element = iterator.next();
// Do what you need to with element
}
除非您在 for-each 循环中无权访问 Iterator。
Iterable 的特定实现可能会改变它实际迭代其元素的方式(它可能使用也可能不使用 Iterator),但它几乎总是归结为一些 for 或 @ 987654334@ 循环。我说“几乎总是”是因为可能涉及某种类型的递归或链接。
现在,当您只是尝试按顺序迭代List 时,使用List.stream().forEach(Consumer) 会创建一个不必要的Stream 对象。如果您确实需要以管道方式处理元素集合(例如映射、过滤、更多映射等),则应仅使用流 API。
因此,对于简单的迭代,使用List.stream().forEach(Consumer) 在几乎所有情况下都比简单的List.forEach(Consumer) 调用性能要差。性能提升很可能可以忽略不计,但“优化”并不过分是一个足够简单的修复;特别是如果您一开始就没有犯“错误”。如果不需要,请勿创建对象。
不过,最好使用 for-each 循环而不是 forEach(Consumer)。它比功能更强大的对应物更容易阅读。
编辑
正如 Holger 在 cmets 中提到的,Stream.forEach(Consumer) 与 Iterable.forEach(Consumer) 有很大的不同:它不保证元素的相遇顺序。虽然Iterable.forEach(Consumer)的迭代顺序也没有为Iterable接口定义,但可以通过扩展接口(如List)来定义。但是,当使用Stream 时,无论Stream 的来源如何,都不能保证顺序。
如果您希望在使用Stream 时保证订单,您必须使用Stream.forEachOrdered(Consumer)。