3.7 获取元素数量
问题
用户希望获取流中元素的数量。
方案
使用 java.util.stream.Stream 接口定义的 count 方法,或 java.util.stream.Collectors 类定义的 counting 方法。
讨论
本范例相当简单,目的是为范例 4.6 讨论的下游收集器作铺垫。
如例 3-39 所示,Stream 接口定义了 count 默认方法,它能返回 long 型数据。
例 3-39 利用
Stream.count方法获取元素数量
- long count = Stream.of(3, 1, 4, 1, 5, 9, 2, 6, 5).count();
- System.out.printf("There are %d elements in the stream%n", count); ➊
➊ 打印 There are 9 elements in the stream
count 方法的有趣之处在于它的实现方式。根据 Javadoc 的描述,“这是一种特殊的归约操作,相当于”:
- return mapToLong(e -> 1L).sum();
首先,流的每个元素都被映射为 1(long)。然后,mapToLong 方法生成 LongStream,它定义了 sum 方法。换言之,先将所有元素映射为 1,再将它们相加,简单明了。
此外,Collectors 类定义了一种类似的方法 counting,如例 3-40 所示。
例 3-40 利用
Collectors.counting方法获取元素数量
count = Stream.of(3, 1, 4, 1, 5, 9, 2, 6, 5).collect(Collectors.counting());System.out.printf("There are %d elements in the stream%n", count);
这两种方法得到的结果并无不同,但既然已有 Stream.count,为什么还要讨论 Collectors.counting 呢?
我们当然可以使用 Stream.count 方法,按说也应该这样处理。不过“下游收集器”(downstream collector)的使用将在范例 4.6 进行详细讨论。就目前而言,我们考虑例 3-41。
例 3-41 对根据长度划分的字符串计数
Map<Boolean, Long> numberLengthMap = strings.stream().collect(Collectors.partitioningBy(s -> s.length() % 2 == 0, ➊Collectors.counting())); ➋numberLengthMap.forEach((k,v) -> System.out.printf("%5s: %d%n", k, v));//// false: 4// true: 8
❶ 谓词
❷ 下游收集器
partitioningBy 方法的第一个参数是 Predicate,其作用是将字符串分为满足谓词和不满足谓词的两类。如果 partitioningBy 方法只有这一个参数,则结果为 Map,其中键为 true 和 false,值为偶数长度和奇数长度字符串的列表。
本例采用 partitioningBy 方法的双参数重载形式,它传入 Predicate 和 Collector。Collector 被称为下游收集器,用于对返回的每个字符串列表进行后期处理。这就是 Collectors.counting 方法的用例。双参数形式的 partitioningBy 方法将输出 Map,其值为流中偶数长度和奇数长度字符串的数量。
Stream 接口定义的其他几种方法在 Collectors 类中也有对应的方法,其他章节将对它们进行讨论。简而言之,直接处理流时请使用 Stream 定义的方法,而 Collectors 定义的方法适用于 partitioningBy 或 groupingBy 操作的下游后期处理。
另见
有关下游收集器的讨论请参见范例 4.6,收集器的一般性介绍请参见第 4 章,有关归约操作的讨论请参见范例 3.3。
