List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Cherry");
list.add("Peach");
List<String> collect = list.stream()
.peek(x -> System.out.println(x))
.filter(x -> x.length()>4)
.peek(x -> System.out.println(x))
.collect(Collectors.toList());
这将打印:
Apple
Apple
Cherry
Cherry
Peach
Peach
所以我的结论是:
流的第一个输入将被处理,直到流结束。 只有这样,流的第二个输入才会被处理,直到流结束。
这与普通的方法调用完全不同。通常您会期望所有输入都将在一个方法调用中得到处理。如果你调用 peek() 我希望我会看到
Apple
Cherry
Peach
但事实并非如此。
因此,stream() 的实现将是它内部有一个 for 循环,并仅使用单个输入连续调用以下方法链。
流是专门为延迟评估而构建的。换句话说:流尝试执行尽可能少的操作来获得终端操作的所需结果(如果您不执行任何终端操作,那么流通常根本不会执行任何操作)。
这在 Stream
流是惰性的;仅在启动终端操作时才对源数据进行计算,并且仅在需要时消耗源元素。
终端操作是包含文本“这是终端操作”的操作。
我将根据问题在这里写另一个例子:
var list = List.of("Apple", "Cherry", "Peach");
var outputList = list.stream()
.peek(x -> System.out.println(x))
.filter(x -> x.length()>5)
.peek(x -> System.out.println(x))
.collect(Collectors.toList());
System.out.println("\n"+outputList);
这将打印
Apple
Cherry
Cherry
Peach
[Cherry]
你得到这个是因为流定义了一个由后续中间操作(在本例中是 peek、filter 和 peek)和最终终端操作(在本例中是收集)组成的管道;当执行终端操作时,源中的每个对象都会通过管道,一个接一个地传递。
在这种情况下,例如,您将有
"Apple" => peek(print "Apple") => filter(is "Apple" longer than 5 chars) ? NO, stop
"Cherry" => peek(print "Cherry") => filter(is "Cherry" longer than 5 chars) ? YES, continue => peek(print "Cherry") => collect(add to the output list)
"Peach" => peek(print "Peach") => filter(is "Peach" longer than 5 chars) ? NO, stop