31

I'd like to iterate two Java-8-Streams together, so that I have in each iteration-step two arguments. Something like that, where somefunction produces something like Stream<Pair<A,B>>.

Stream<A> as;
Stream<B> bs;
somefunction (as, bs)
  .forEach ((a, b) -> foo (a, b));
// or something like
somefunction (as, bs)
  .forEach ((Pair<A, B> abs) -> foo (abs.left (), abs.right ()));

I want to know, if Java provides something like that, although there is no Pair in Java :-( If there is no API-Function like that, is there another way of iterating two streams simultaniously?

F. Böller
  • 4,194
  • 2
  • 20
  • 32

1 Answers1

27
static <A, B> Stream<Pair<A, B>> zip(Stream<A> as, Stream<B> bs)
{
    Iterator<A> i=as.iterator();
    return bs.filter(x->i.hasNext()).map(b->new Pair<>(i.next(), b));
}

This does not offer parallel execution but neither did the original zip implementation.

And as F. Böller has pointed out it doesn’t work if bs is infinite and as is not. For a solution which works for all possible combinations of infinite and finite streams, an intermediate Iterator which checks both sources within the hasNext method seems unavoidable¹:

static <A, B> Stream<Pair<A,B>> zip(Stream<A> as, Stream<B> bs) {
    Iterator<A> i1 = as.iterator();
    Iterator<B> i2 = bs.iterator();
    Iterable<Pair<A,B>> i=()->new Iterator<Pair<A,B>>() {
        public boolean hasNext() {
            return i1.hasNext() && i2.hasNext();
        }
        public Pair<A,B> next() {
            return new Pair<A,B>(i1.next(), i2.next());
        }
    };
    return StreamSupport.stream(i.spliterator(), false);
}

If you want parallel capable zipping you should consider the source of the Stream. E.g. you can zip two ArrayLists (or any RandomAccessList) like

ArrayList<Foo> l1=new ArrayList<>();
ArrayList<Bar> l2=new ArrayList<>();
IntStream.range(0, Math.min(l1.size(), l2.size()))
         .mapToObj(i->new Pair(l1.get(i), l2.get(i)))
         . …

¹(unless you implement a Spliterator directly)

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
  • Just found out, that this solution is not working for an infinite Stream of Bs. In this case it's an endless loop. The B-Stream has to be limited in some way. However, I didn't find a solution with small changes to your above code. The linked answer seems to work with infinite streams too: [link](http://stackoverflow.com/a/23529010/3699131) – F. Böller Aug 05 '14 at 06:41
  • 2
    @F. Böller: right, there is no small change solving this. The solution you have linked might work but I recommend to be careful about licensing issues as the code has been c&p'ed from a beta jdk source as the answerer says by itself. You should also keep in mind that there might be other issues as the authors of the code might have had a reason to remove it from the jdk… – Holger Aug 05 '14 at 08:57