3.7 获取元素数量

问题

用户希望获取流中元素的数量。

方案

使用 java.util.stream.Stream 接口定义的 count 方法,或 java.util.stream.Collectors 类定义的 counting 方法。

讨论

本范例相当简单,目的是为范例 4.6 讨论的下游收集器作铺垫。

如例 3-39 所示,Stream 接口定义了 count 默认方法,它能返回 long 型数据。

例 3-39 利用 Stream.count 方法获取元素数量

  1. long count = Stream.of(3, 1, 4, 1, 5, 9, 2, 6, 5).count();
  2. System.out.printf("There are %d elements in the stream%n", count);

➊ 打印 There are 9 elements in the stream

count 方法的有趣之处在于它的实现方式。根据 Javadoc 的描述,“这是一种特殊的归约操作,相当于”:

  1. return mapToLong(e -> 1L).sum();

首先,流的每个元素都被映射为 1(long)。然后,mapToLong 方法生成 LongStream,它定义了 sum 方法。换言之,先将所有元素映射为 1,再将它们相加,简单明了。

此外,Collectors 类定义了一种类似的方法 counting,如例 3-40 所示。

例 3-40 利用 Collectors.counting 方法获取元素数量

  1. count = Stream.of(3, 1, 4, 1, 5, 9, 2, 6, 5)
  2. .collect(Collectors.counting());
  3. 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 对根据长度划分的字符串计数

  1. Map<Boolean, Long> numberLengthMap = strings.stream()
  2. .collect(Collectors.partitioningBy(
  3. s -> s.length() % 2 == 0,
  4. Collectors.counting()));
  5. numberLengthMap.forEach((k,v) -> System.out.printf("%5s: %d%n", k, v));
  6. //
  7. // false: 4
  8. // true: 8

❶ 谓词

❷ 下游收集器

partitioningBy 方法的第一个参数是 Predicate,其作用是将字符串分为满足谓词和不满足谓词的两类。如果 partitioningBy 方法只有这一个参数,则结果为 Map>,其中键为 truefalse,值为偶数长度和奇数长度字符串的列表。

本例采用 partitioningBy 方法的双参数重载形式,它传入 PredicateCollectorCollector 被称为下游收集器,用于对返回的每个字符串列表进行后期处理。这就是 Collectors.counting 方法的用例。双参数形式的 partitioningBy 方法将输出 Map,其值为流中偶数长度和奇数长度字符串的数量。

Stream 接口定义的其他几种方法在 Collectors 类中也有对应的方法,其他章节将对它们进行讨论。简而言之,直接处理流时请使用 Stream 定义的方法,而 Collectors 定义的方法适用于 partitioningBygroupingBy 操作的下游后期处理。

另见

有关下游收集器的讨论请参见范例 4.6,收集器的一般性介绍请参见第 4 章,有关归约操作的讨论请参见范例 3.3。