首先,让我们对流有一个基本的了解。 然后,我们将研究在使用流时可能发生的副作用。
流表示来自源的一系列对象,支持聚合操作。 在使用流时要通知的一件事是,对聚合操作(中间操作)进行了延迟评估,即它们直到终端操作开始才开始处理流的内容。 这使Java编译器和运行时能够优化它们如何处理流。
- 向SportsCastr社区大喊:LANE DASCHKO和MR RED ZONE REPLAY
- “东亚特兰大情书”拼写了对6lack的所有爱
- Spotify不了解Facebook共享的个人用户数据; SiriusXM将在除夕直播音乐会
- 流媒体:迪士尼迪斯尼鲍勃·伊格
- 我对音乐行业的看法与今天一样
使用Java 8,Collection接口具有两种生成Stream的方法。
- stream() -返回一个以集合为源的顺序流。
下面给出了一个非常基本的示例,其中小于5的double类型的数组元素被转换为整数。 由于我们正在使用流,因此接收到的输出顺序是固定的。
[代码语言=“ java”]
/ **
*流的基本示例
* /
Double [] doubleArray = {1.8,2.0,3.2,4.5,5.6,6.0,7.0,8.9};
List listOfDouble =
新的ArrayList (Arrays.asList(doubleArray));
listOfDouble
。流()
.filter(element-> element value.intValue())。
forEach(System.out :: println);
[/码]
- parallelStream() -返回一个以集合为源的并行Stream。
并行计算包括将一个问题分解为多个子问题,同时解决这些问题(并行处理,每个子问题在单独的线程中运行),然后将解决方案的结果组合到子问题中。 当流并行执行时,Java运行时将流划分为多个子流。 聚合操作迭代并并行处理这些子流,然后合并结果。
现在,在下面的示例中,我们使用并行流而不是流。 元素的评估顺序可以在每次运行时变化。
[代码语言=“ java”]
/ **
*并行流的基本示例
* /
Double [] doubleArray = {1.8,2.0,3.2,4.5,5.6,6.0,7.0,8.9};
List listOfDouble =
新的ArrayList (Arrays.asList(doubleArray));
listOfDouble
.parallelStream()
.filter(element-> element value.intValue())。
forEach(System.out :: println);
[/码]
副作用 :
- 干扰
谈到副作用,流操作中的Lambda表达式不应产生干扰 。 在管道处理流时修改流的源时会发生干扰。
[代码语言=“ java”]
/ **
*干扰示例
*这将通过ConcurrentModificationException
* /
尝试{
List listOfStrings =
新的ArrayList (Arrays.asList(“ one”,“ two”,“ four”)));
listOfStrings
.parallelStream()
.map(s-> {
listOfStrings.add(“三”);
返回listOfStrings;
})
.forEach(System.out :: println);
} catch(Exception e){
System.out.println(“捕获到的异常:“ + e.toString());
}
[/码]
上面的示例导致ConcurrentModificationException。 即使我们将使用流而不是并行流,也会发生相同的情况。 这种行为的原因是懒惰的评估。 这意味着此示例中的管道在调用操作get时开始执行,并在get操作完成时结束执行。 在管道执行期间尝试修改流源时,它将引发运行时异常。
- 有状态Lambda表达式
有状态lambda表达式是一种有状态的lambda表达式,其结果取决于在管道执行期间可能更改的任何状态。 对以下示例的理解将使您对该定义有清楚的了解。
[代码语言=“ java”]
/ **
*有状态lambda表达式示例
* /
Integer [] intArray = {1,2,3,4,5,6,7,8};
List listOfIntegers =
新的ArrayList (Arrays.asList(intArray));
List serialStorage =新的ArrayList ();
System.out.println(“串行流:”);
listOfIntegers
。流()
//它使用有状态的lambda表达式。 不应该这样做!
.map(e-> {
serialStorage.add(e);
返回e;
})
.forEachOrdered(e-> System.out.print(e +“”));
System.out.println(“”);
串行存储
。流()
.forEachOrdered(e-> System.out.print(e +“”));
System.out.println(“”);
System.out.println(“并行流:”);
List parallelStorage = Collections.synchronizedList(
new ArrayList ());
listOfIntegers
.parallelStream()
//它使用有状态的lambda表达式。 不应该这样做!
.map(e-> {
parallelStorage.add(e);
返回e;
})
.forEachOrdered(e-> System.out.print(e +“”));
System.out.println(“”);
并行存储
。流()
.forEachOrdered(e-> System.out.print(e +“”));
System.out.println(“”);
}
[/码]
Lambda表达式e-> {parallelStorage.add(e); 返回e; }是有状态的lambda表达式。 每次运行代码时,其结果可能会有所不同。 本示例打印以下内容:
串行流:
8 7 6 5 4 3 2 1
8 7 6 5 4 3 2 1
并行流:
8 7 6 5 4 3 2 1
1 3 6 2 4 5 8 7
无论流是以串行还是并行方式执行,forEachOrdered的操作均按流指定的顺序处理元素。 但是,要记住的一点是,当并行执行流时,映射操作将处理Java运行时和编译器指定的流元素。 因此,lambda表达式e-> {parallelStorage.add(e); 返回e; 每次向代码添加元素时,parallelStorage可能会有所不同。 为了获得确定性和可预测的结果,请确保流操作中的lambda表达式参数不是有状态的。
注意:流的最重要特征之一是它们只能运行一次,即不允许流的可重用性。
参考文献:
Java™教程
Java编程语言的教程和参考指南
docs.oracle.com
