这可以使用一个收集器来完成。
- 对于两个类别,使用
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)
方法就不可用了。你必须手动地完成,没有收集器工厂。它的实现看起来像这样:
[自 2020-04-16 起的示例 2.0]
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
NT空间(N = 初始元素数,T = 线程数)。为了在速度和空间之间取得平衡,您可以省略它或使用您最好的猜测,比如一个分区中预期的最高元素数量(通常略高于平衡分割的N/2)。
我希望我的做法不会冒犯任何人,因为我使用了Java 9的方法。对于Java 8版本,请查看编辑历史记录。
Stream
转换为多个Stream
而不进行中间转换的可能性,尽管我认为达到这个问题的人实际上正在寻找实现此目标的方法,而不考虑这种限制,这是Mark的答案。这可能是因为标题中的问题与描述中的问题不同。 - devildelta