guava/src/com/google/common/collect/Streams.java
@Beta
  public static <A, B, R> Stream<R> zip(
      Stream<A> streamA, Stream<B> streamB, BiFunction<? super A, ? super B, R> function) {
    checkNotNull(streamA);
    checkNotNull(streamB);
    checkNotNull(function);
    boolean isParallel = streamA.isParallel() || streamB.isParallel(); // same as Stream.concat
    Spliterator<A> splitrA = streamA.spliterator();
    Spliterator<B> splitrB = streamB.spliterator();
    int characteristics =
        splitrA.characteristics()
            & splitrB.characteristics()
            & (Spliterator.SIZED | Spliterator.ORDERED);
    Iterator<A> itrA = Spliterators.iterator(splitrA);
    Iterator<B> itrB = Spliterators.iterator(splitrB);
    return StreamSupport.stream(
            new AbstractSpliterator<R>(
                Math.min(splitrA.estimateSize(), splitrB.estimateSize()), characteristics) {
              @Override
              public boolean tryAdvance(Consumer<? super R> action) {
                if (itrA.hasNext() && itrB.hasNext()) {
                  action.accept(function.apply(itrA.next(), itrB.next()));
                  return true;
                }
                return false;
              }
            },
            isParallel)
        .onClose(streamA::close)
        .onClose(streamB::close);
  }
@Beta
  public static <A, B, R> Stream<R> zip(
      Stream<A> streamA, Stream<B> streamB, BiFunction<? super A, ? super B, R> function) {
    checkNotNull(streamA);
    checkNotNull(streamB);
    checkNotNull(function);
    boolean isParallel = streamA.isParallel() || streamB.isParallel(); // same as Stream.concat
    Spliterator<A> splitrA = streamA.spliterator();
    Spliterator<B> splitrB = streamB.spliterator();
    int characteristics =
        splitrA.characteristics()
            & splitrB.characteristics()
            & (Spliterator.SIZED | Spliterator.ORDERED);
    Iterator<A> itrA = Spliterators.iterator(splitrA);
    Iterator<B> itrB = Spliterators.iterator(splitrB);
    return StreamSupport.stream(
            new AbstractSpliterator<R>(
                Math.min(splitrA.estimateSize(), splitrB.estimateSize()), characteristics) {
              @Override
              public boolean tryAdvance(Consumer<? super R> action) {
                if (itrA.hasNext() && itrB.hasNext()) {
                  action.accept(function.apply(itrA.next(), itrB.next()));
                  return true;
                }
                return false;
              }
            },
            isParallel)
        .onClose(streamA::close)
        .onClose(streamB::close);
  }
@ Beta public static < A , B , R > Stream < R > zip ( Stream < A > streamA , Stream < B > streamB , BiFunction < ? super A , ? super B , R > function ) { checkNotNull ( streamA ) ; checkNotNull ( streamB ) ; checkNotNull ( function ) ; boolean isParallel = streamA . isParallel ( ) || streamB . isParallel ( ) ; // same as Stream.concat Spliterator < A > splitrA = streamA . spliterator ( ) ; Spliterator < B > splitrB = streamB . spliterator ( ) ; int characteristics = splitrA . characteristics ( ) & splitrB . characteristics ( ) & ( Spliterator . SIZED | Spliterator . ORDERED ) ; Iterator < A > itrA = Spliterators . iterator ( splitrA ) ; Iterator < B > itrB = Spliterators . iterator ( splitrB ) ; return StreamSupport . stream ( new AbstractSpliterator < R > ( Math . min ( splitrA . estimateSize ( ) , splitrB . estimateSize ( ) ) , characteristics ) { @ Override public boolean tryAdvance ( Consumer < ? super R > action ) { if ( itrA . hasNext ( ) && itrB . hasNext ( ) ) { action . accept ( function . apply ( itrA . next ( ) , itrB . next ( ) ) ) ; return true ; } return false ; } } , isParallel ) . onClose ( streamA :: close ) . onClose ( streamB :: close ) ; }
Returns a stream in which each element is the result of passing the corresponding elementY of
each of {@code streamA} and {@code streamB} to {@code function}.

<p>For example:

<pre>{@code
Streams.zip(
Stream.of("foo1", "foo2", "foo3"),
Stream.of("bar1", "bar2"),
(arg1, arg2) -> arg1 + ":" + arg2)
}</pre>

<p>will return {@code Stream.of("foo1:bar1", "foo2:bar2")}.

<p>The resulting stream will only be as long as the shorter of the two input streams; if one
stream is longer, its extra elements will be ignored.

<p>Note that if you are calling {@link Stream#forEach} on the resulting stream, you might want
to consider using {@link #forEachPair} instead of this method.

<p><b>Performance note:</b> The resulting stream is not <a
href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>.
This may harm parallel performance.
Returns a stream in which each element is the result of passing the corresponding elementY of each of { @code streamA } and { @code streamB } to { @code function } .