为此可以使用收集器。
- 对于两个类别,使用
Collectors.partitioningBy()
工厂。
这将创建一个Map<Boolean, List>
,并根据一个将项目放在一个或另一个列表中Predicate
。
注意:由于流需要被整个消费,这不能在无限流上工作。而且因为流无论如何都会被消耗,所以这个方法只是将它们放在列表中,而不是创建一个新的带内存的流。如果您需要流作为输出,您始终可以流式传输这些列表。
此外,不需要迭代器,即使在您提供的仅限头部的示例中也是如此。
Random r = new Random();
Map<Boolean, List<String>> groups = stream
.collect(Collectors.partitioningBy(x -> r.nextBoolean()));
System.out.println(groups.get(false).size());
System.out.println(groups.get(true).size());
- 对于更多类别,请使用
Collectors.groupingBy()
工厂。
Map<Object, List<String>> groups = stream
.collect(Collectors.groupingBy(x -> r.nextInt(3)));
System.out.println(groups.get(0).size());
System.out.println(groups.get(1).size());
System.out.println(groups.get(2).size());
如果流不是Stream
,而是原始流之一,例如IntStream
,则此.collect(Collectors)
方法不可用。您必须在没有收集器工厂的情况下手动进行。它的实现如下所示:
[示例 2.0 自 2020-04-16 起]
IntStream intStream = IntStream.iterate(0, i -> i + 1).limit(100000).parallel();
IntPredicate predicate = ignored -> r.nextBoolean();
Map<Boolean, List<Integer>> groups = intStream.collect(
() -> Map.of(false, new ArrayList<>(100000),
true , new ArrayList<>(100000)),
(map, value) -> map.get(predicate.test(value)).add(value),
(map1, map2) -> {
map1.get(false).addAll(map2.get(false));
map1.get(true ).addAll(map2.get(true ));
});
在此示例中,我使用初始集合的完整大小初始化 ArrayLists(如果完全知道的话)。即使在最坏的情况下,这也可以防止调整大小事件,但可能会占用 2 N T 空间(N = 初始元素数,T = 线程数)。为了以空间换取速度,您可以忽略它或使用您最好的猜测,例如一个分区中预期的最高元素数(通常刚好超过 N/2 以实现平衡拆分)。
我希望我不会因为使用 Java 9 方法而冒犯任何人。对于 Java 8 版本,请查看编辑历史记录。