Java8 Stream的副作用

懒加载

    基于编译器优化的考虑,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

猜你喜欢

转载自blog.csdn.net/weixin_38569499/article/details/87876814