问题
我有一个由Java 8流表示的数据集:
Stream<T> stream = ...;
我可以看到如何过滤它以获得随机子集 - 例如
Random r = new Random();
PrimitiveIterator.OfInt coin = r.ints(0, 2).iterator();
Stream<T> heads = stream.filter((x) -> (coin.nextInt() == 0));
我还可以看到如何减少此流以获取,例如,两个列表代表数据集的两个随机半部分,然后将它们转换回流。但是,有没有直接的方法从最初的流生成两个流?就像是
(heads, tails) = stream.[some kind of split based on filter]
感谢你的任何见解。
#1 热门回答(196 赞)
可以使用Acollector。
- 对于两个类别,请使用Collectors.partitioningBy()工厂。
这将创建一个Map
从Boolean
到List
,并根据aPredicate
将项目放在一个或另一个列表中。
注意:由于流需要整体使用,因此无法在无限流上运行。因为无论如何都要使用流,所以此方法只是将它们放入列表中,而不是使用内存创建新的流。
此外,不需要迭代器,甚至不需要你提供的头部示例。
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)
方法不可用。没有收集器工厂,你必须以手动方式完成。它的实现看起来像这样:
IntStream intStream = IntStream.iterate(0, i -> i + 1).limit(1000000);
Predicate<Integer> p = x -> r.nextBoolean();
Map<Boolean, List<Integer>> groups = intStream.collect(() -> {
Map<Boolean, List<Integer>> map = new HashMap<>();
map.put(false, new ArrayList<>());
map.put(true, new ArrayList<>());
return map;
}, (map, x) -> {
boolean partition = p.test(x);
List<Integer> list = map.get(partition);
list.add(x);
}, (map1, map2) -> {
map1.get(false).addAll(map2.get(false));
map1.get(true).addAll(map2.get(true));
});
System.out.println(groups.get(false).size());
System.out.println(groups.get(true).size());
编辑
正如所指出的,上述"解决方法"不是线程安全的。在收集之前转换为正常Stream
是要走的路:
Stream<Integer> stream = intStream.boxed();
#2 热门回答(16 赞)
不幸的是,你所要求的是在JavaDoc of Stream中直接不赞成的:
只应对一个流进行操作(调用中间或终端流操作)一次。例如,这排除了"分叉"流,其中相同的源提供两个或更多个管道,或者同一个流的多个遍历。
如果你真的希望这种行为,你可以使用peek
或其他方法解决这个问题。在这种情况下,你应该做的不是尝试使用分叉过滤器从同一原始Stream源备份两个流,而是复制流并适当地过滤每个重复项。
但是,你可能希望重新考虑aStream
是否适合你的用例。
#3 热门回答(9 赞)
不完全是。你不可能得到两个Stream
;这没有意义 - 如何在不需要同时生成另一个的情况下迭代一个?流只能运行一次。
但是,如果要将它们转储到列表或其他内容中,则可以执行此操作
stream.forEach((x) -> ((x == 0) ? heads : tails).add(x));