java – Can you split a stream into two streams?

A binder can be used for this purpose .

  • For two categories, use Collectors.partitioningBy() factory.

This will create a Map from Boolean to List and will insert items into one or the other list based on Predicate.

Note: Since the stream must be consumed whole, this cannot work on infinite streams. Since the stream is still consumed, this method simply inserts them into the lists rather than creating a new stream with memory.

Also, you don’t need the iterator, not even in the head-only example you provided.

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());
  • For multiple categories, use one Collectors.groupingBy() factory.
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());

In case the flows are not Streambut one of the primitive streams like IntStreamthis method .collect(Collectors) not available. You will have to do this manually without a collector factory. Its implementation looks like this:

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());

Edit

As pointed out, the “workaround” above is not thread safe. Converting to a normal Stream before harvesting is the way to go:

Stream<Integer> stream = intStream.boxed();

Leave a comment