这是一个简短的节目,展示了(可以说)主要的差异化因素。
public static void main(String[] args) {
List<Integer> input = Arrays.asList(10, 12, 13, 15, 17, 19);
List<Integer> list = pickEvensViaLists(input);
for (int i = 0; i < 2; ++i)
System.out.println(list.get(i));
System.out.println("--------------------------------------------");
pickEvensViaStreams(input).limit(2).forEach((x) -> System.out.println(x));
}
private static List<Integer> pickEvensViaLists(List<Integer> input) {
List<Integer> list = new ArrayList<Integer>(input);
for (Iterator<Integer> iter = list.iterator(); iter.hasNext(); ) {
int curr = iter.next();
System.out.println("processing list element " + curr);
if (curr % 2 != 0)
iter.remove();
}
return list;
}
private static Stream<Integer> pickEvensViaStreams(List<Integer> input) {
Stream<Integer> inputStream = input.stream();
Stream<Integer> filtered = inputStream.filter((curr) -> {
System.out.println("processing stream element " + curr);
return curr % 2 == 0;
});
return filtered;
}
这个程序接受一个输入列表并从中打印前两个偶数。它这样做了两次:第一次使用带有手写循环的列表,第二次使用带有 lambda 表达式的流。
在两种方法中必须编写的代码量方面存在一些差异,但这不是(在我看来)重点。不同之处在于评估的方式:
在基于列表的方法中,pickEvensViaLists() 的代码会遍历整个列表。它将从列表中删除所有奇数值,然后才会返回到main()。因此,它返回给main() 的列表将包含四个值:10, 12, 20, 30 和main() 将只打印前两个。
在基于流的方法中,pickEvensViaStreams() 的代码实际上并不迭代任何东西。它返回一个可以从输入流中计算出来的流,但它还没有计算任何一个。只有当main() 开始迭代(通过forEach())时,返回的流的元素才会被一一计算。由于main() 只关心前两个元素,因此实际上只计算了返回流的两个元素。换句话说:使用流,您会得到惰性评估:流只在需要时迭代。
我们来看看这个程序的输出:
--------------------------------------------
list-based filtering:
processing list element 10
processing list element 12
processing list element 13
processing list element 15
processing list element 17
processing list element 19
processing list element 20
processing list element 30
10
12
--------------------------------------------
stream-based filtering:
processing stream element 10
10
processing stream element 12
12
使用列表迭代整个输入(因此有八个“处理列表元素”消息)。使用流时,实际上只从输入中提取了两个元素,导致只有两个“处理流元素”消息。