你能把一个流分成两个流吗?

新手上路,请多包涵

我有一个由 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]

感谢您的任何见解。

原文由 user1148758 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 444
2 个回答

不完全是。你不能从一个中得到两个 Stream ;这没有意义——您如何在不需要同时生成另一个的情况下迭代一个?一个流只能操作一次。

但是,如果您想将它们转储到列表或其他内容中,您可以这样做

stream.forEach((x) -> ((x == 0) ? heads : tails).add(x));

原文由 Louis Wasserman 发布,翻译遵循 CC BY-SA 3.0 许可协议

为此可以使用 _收集器_。

  • 对于两个类别,使用 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 版本,查看编辑历史。

原文由 Mark Jeronimus 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题