1

I have two Java Stream<String> A and B.

How can I, at each step, given a predicate p, pick an element from either A or B? The element that has not been picked has to stay at the head of the stream so it can be picked at the next try.

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
DennisVDB
  • 1,347
  • 1
  • 14
  • 30

2 Answers2

1

Use can use a zip method. The standard library doesn't include one as standard, but you can just copy the source shown below (from this question).

import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;


class Main {
    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("a1", "a2", "a3");
        List<String> list2 = Arrays.asList("b1", "b2", "b3");

        BiFunction<String, String, String> picker = (a, b) -> {
            // pick whether you want a from list1, or b from list2
            return a;
        };

        List<String> result = 
            StreamUtils.zip(list1.stream(), list2.stream(), picker)
            .collect(Collectors.toList());

        System.out.println(result);
    }
}

StreamUtils.java

import java.util.Objects;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import java.util.function.BiFunction;

public class StreamUtils {
    public static<A, B, C> Stream<C> zip(Stream<? extends A> a,
                                         Stream<? extends B> b,
                                         BiFunction<? super A, ? super B, ? extends C> zipper) {
        Objects.requireNonNull(zipper);
        Spliterator<? extends A> aSpliterator = Objects.requireNonNull(a).spliterator();
        Spliterator<? extends B> bSpliterator = Objects.requireNonNull(b).spliterator();

        // Zipping looses DISTINCT and SORTED characteristics
        int characteristics = aSpliterator.characteristics() & bSpliterator.characteristics() &
                ~(Spliterator.DISTINCT | Spliterator.SORTED);

        long zipSize = ((characteristics & Spliterator.SIZED) != 0)
                ? Math.min(aSpliterator.getExactSizeIfKnown(), bSpliterator.getExactSizeIfKnown())
                : -1;

        Iterator<A> aIterator = Spliterators.iterator(aSpliterator);
        Iterator<B> bIterator = Spliterators.iterator(bSpliterator);
        Iterator<C> cIterator = new Iterator<C>() {
            @Override
            public boolean hasNext() {
                return aIterator.hasNext() && bIterator.hasNext();
            }

            @Override
            public C next() {
                return zipper.apply(aIterator.next(), bIterator.next());
            }
        };

        Spliterator<C> split = Spliterators.spliterator(cIterator, zipSize, characteristics);
        return (a.isParallel() || b.isParallel())
               ? StreamSupport.stream(split, true)
               : StreamSupport.stream(split, false);
    }
}
Community
  • 1
  • 1
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • There is no sense in a construct like `Objects.requireNonNull(a).spliterator()`. All `requireNonNull` does, is throwing a `NullPointerException` if `a` is `null`, which is what happens anyway, when you just call `a.spliterator()`. Further, you can use `long zipSize = Math.min(aSpliterator.getExactSizeIfKnown(), bSpliterator.getExactSizeIfKnown());` as it will be `-1` anyway, if either stream has no `SIZED` property. And the return statement can be simplified to `return StreamSupport.stream(split, a.isParallel() || b.isParallel());`, no need for an additional conditional expression. – Holger Mar 08 '17 at 09:15
  • @Holger This is code copied from the answer I linked in my question. Apparently it was originally taken from the JDK source before this API was made private in the 1.8 release. – Alexander Mar 08 '17 at 13:52
  • What do you mean with “before this API was made private”? Do you claim there was a secret `zip` method within the JDK? Besides that, everyone copying abandoned code must be aware that (s)he is now the new maintainer. – Holger Mar 08 '17 at 14:05
  • By the way, this code does not fulfill the OP’s requirement, “*The element that has not been picked has to stay at the head of the stream so it can be picked at the next try.*” – Holger Mar 08 '17 at 14:06
  • @Holger Yes, there is such an API. It's [`Streams.zip`](http://download.java.net/lambda/b93/docs/api/java/util/stream/Streams.html), which was public in b93, but is now private in 1.8. – Alexander Mar 08 '17 at 14:09
  • I don’t question that there was such a method in beta93, I question your claim that it is “now private”. It has been *removed*, which is a different thing than making something `private`. There is no `zip` method anymore. – Holger Mar 08 '17 at 15:25
  • Iirc, trying to use it gave me an error about it's access level. I think the `Streams` class as a whole has package access. – Alexander Mar 08 '17 at 15:26
  • There is a non-`public` class with that name, but it has no `zip` method. After removing the `zip` method and moving the `concat` methods to the `Stream` interfaces, there was nothing left that would justify keeping this class `public`. – Holger Mar 08 '17 at 15:28
  • @Holger Okay, so it was deleted, not made private. Not sure where you're going with that – Alexander Mar 08 '17 at 15:35
0

If the two input streams are already sorted by the merge function, the new stream will be sorted by the merge function eventually. It's going to be something like merge sort. So you just need to concat the two streams, sorted then.

final Stream<String> a = Stream.of("a", "b", "c");
final Stream<String> b = Stream.of("1", "2", "3");        
Stream.concat(a, b).sorted((a, b) -> mergeFunction /* TODO */);

Otherwise, probably you will need the stream API in abacus-common:

final Stream<String> a = Stream.of("a", "b", "c");
final Stream<String> b = Stream.of("1", "2", "3");        
Stream.merge(a, b, mergeFunction);
user_3380739
  • 1
  • 14
  • 14