懒加载
基于编译器优化的考虑,stream的中间操作是懒加载的,只有当执行到最终操作(terminal operation)的时候,中间操作才会去执行。这样编译器可以决定按照怎样的顺序去执行流的操作,以获取最高的效率。
避免干扰
避免干扰指的是,在流操作的过程中,一定不要去修改流的数据源!如果有干扰发生了,程序就会抛出异常ConcurrentModificationException。
代码示例:
try {
List<String> listOfStrings =
new ArrayList<>(Arrays.asList("one", "two"));
String concatenatedString = listOfStrings
.stream()
// Don't do this! Interference occurs here.
.peek(s -> listOfStrings.add("three"))
.reduce((a, b) -> a + " " + b)
.get();
System.out.println("Concatenated string: " + concatenatedString);
} catch (Exception e) {
System.out.println("Exception caught: " + e.toString());
}
避免使用有状态的Lambda表达式
有状态的Lambda表达式指的是,结果可能会在管道执行过程中产生变化的表达式。避免使用有状态的lambda表达式,我个人的理解是,避免在stream的中间操作中对lambda表达式之外的属性产生写操作,或者说不要去修改函数外部的状态,尤其是在并行的stream中,这种操作导致的结果是不可预测的,因为并行流是无序的。
代码示例:
List<Integer> serialStorage = new ArrayList<>();
System.out.println("Serial stream:");
listOfIntegers
.stream()
// Don't do this! It uses a stateful lambda expression.
.map(e -> { serialStorage.add(e); return e; })
.forEachOrdered(e -> System.out.print(e + " "));
System.out.println("");
serialStorage
.stream()
.forEachOrdered(e -> System.out.print(e + " "));
System.out.println("");
System.out.println("Parallel stream:");
List<Integer> parallelStorage = Collections.synchronizedList(
new ArrayList<>());
listOfIntegers
.parallelStream()
// Don't do this! It uses a stateful lambda expression.
.map(e -> { parallelStorage.add(e); return e; })
.forEachOrdered(e -> System.out.print(e + " "));
System.out.println("");
parallelStorage
.stream()
.forEachOrdered(e -> System.out.print(e + " "));
System.out.println("");
执行结果:
Serial stream:
8 7 6 5 4 3 2 1
8 7 6 5 4 3 2 1
Parallel stream:
8 7 6 5 4 3 2 1
1 3 6 2 4 5 8 7
说明:在并行流中,map函数处理元素的顺序取决于JRE和编译器,因此操作e -> { parallelStorage.add(e); return e; }的执行顺序是无法预测的,即使终端操作是forEachOrdered,也不能保证中间操作的顺序。所以,添加的List parallelStorage中的元素的顺序,就出现了上面展示的奇怪的顺序。
参考链接:官方文档 side_effects