Java 8 的 Lambda 表达式和流处理(2)流
- UID
- 1066743
|
Java 8 的 Lambda 表达式和流处理(2)流
流Java 8 中的流表示的是元素的序列。流中的元素可能是对象、int、long 或 double 类型。流作为一个高层次的抽象,并不关注流中元素的来源或是管理方式。流只关注对流中元素所进行的操作。当流与函数式接口和 Lambda 表达式一同使用时,可以写出简洁高效的数据处理代码。下面介绍几个与流相关的基本概念。
顺序执行和 并行执行流的操作可以顺序执行或并行执行, 后者可以获得比前者更好的性能。但是如果实现不当,可能由于数据竞争或无用的线程同步,导致并行执行时的性能更差。一个流是否会并行执行,可以通过其方法 isParallel() 来判断。根据流的创建方式,一个流有其默认的执行方式。可以使用方法 sequential() 或 parallel() 来将其执行方式设置为顺序或并行。
相遇顺序一个流的相遇顺序(encounter order)是流中的元素被处理时的顺序。流根据其特征可能有,也可能没有一个确定的相遇顺序。举例来说,从 ArrayList 创建的流有确定的相遇顺序;从 HashSet 创建的流没有确定的相遇顺序。大部分的流操作会按照流的相遇顺序来依次处理元素。如果一个流是无序的,同一个流处理流水线在多次执行时可能产生不一样的结果。比如 Stream 的 findFirst() 方法获取到流中的第一个元素。如果在从 ArrayList 创建的流上应用该操作,返回的总是第一个元素;如果是从 HashSet 创建的流,则返回的结果是不确定的。对于一个无序的流,可以使用 sorted 操作来排序;对于一个有序的流,可以使用 unordered() 方法来使其无序。
Spliterator所有的流都是从 Spliterator 创建出来的。Spliterator 的名称来源于它所支持的两种操作:split 和 iterator。Spliterator 可以看成是 Iterator 的并行版本,允许通过对流中元素分片的方式来切分数据源。使用其 tryAdvance 方法来顺序遍历元素,也可以使用 trySplit 方法来创建一个新的 Spliterator 对象在新划分的数据集上工作。Spliterator 还提供了 forEachRemaining 方法进行批量顺序遍历。可以使用 estimateSize 方法来查询可能会遍历的元素数量。一般的做法是先使用 trySplit 切分数据源。当元素数量足够小时,使用 forEachRemaining 来对分片中的全部元素进行处理。这也是典型的分治法的思路。
每个 Spliterator 可以有一系列不同的特征,可以通过 characteristics 方法来查询。一个 Spliterator 具备的特征取决于其数据源和元素。所有可用的特征如下所示:
- CONCURRENT:表明数据源可以安全地由多个线程进行修改,而无需额外的同步机制。
- DISTINCT:表明数据源中的元素是唯一的,不存在重复元素。
- IMMUTABLE:表明数据源是不可变的, 无法进行修改操作。
- NONNULL:表明数据源中不存在 null 元素。
- ORDERED:表明数据源中的元素有确定的相遇顺序。
- SIZED:表明数据源中的元素的数量是确定的。
- SORTED:表明数据源中的元素是有序的。
- SUBSIZED:表明使用 trySplit 切分出来的子数据源也有 SIZED 和 SUBSIZED 的特征。
Spliterator 需要绑定到流之后才能遍历其中的元素。不同的 Spliterator 实现可能有不同的绑定时机。如果一个 Spliterator 是延迟绑定的,那么只有在进行首次遍历、首次切分或首次查询大小时,才会绑定到流上;反之,它会在创建时或首次调用任何方法时绑定到流上。绑定时机的重要性在于,在绑定之前对流所做的修改,在 Spliterator 遍历时是可见的。延迟绑定可以提供最大限度的灵活性。
有状态和无状态操作 流操作可以是有状态或无状态的。当一个有状态的操作在处理一个元素时,它可能需要使用处理之前的元素时保留的信息;无状态的操作可以独立处理每个元素,举例来说:
- distinct 和 sorted 是有状态操作的例子。distinct 操作从流中删除重复元素,它需要记录下之前已经遇到过的元素来确定当前元素是否应该被删除。sorted 操作对流进行排序,它需要知道所有元素来确定当前元素在排序之后的所在位置。
- filter 和 map 是无状态操作的例子。filter 操作在进行过滤时只需要看当前元素即可。map 操作可以独立转换当前元素。一般来说,有状态操作的运行代价要高于无状态操作,因为需要额外的空间保存中间状态信息。
Stream<T> 是表示流的接口,T 是流中元素的类型。对于原始类型的流,可以使用专门的类 IntStream、LongStream 和 DoubleStream。 |
|
|
|
|
|