// use any package you like! package ch.claude-martin.zip; import java.util.Objects; import java.util.Spliterator; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * This code is from an early build of Java 8 Beta. * *

* This was removed later and no zip function exists in the final Java 8 release: changeset * 8789:569088178418. *

* Discussion on Stackoverflow. * */ public final class StreamZipper { /** A very simple Pair that can be used to zip two streams. * * @author Claude Martin */ public static final class Pair { /** First element. */ public final A first; /** Second element. */ public final B second; public Pair(A first, B second) { super(); this.first = first; this.second = second; } /** * Scala-style getter for {@link #first}. * * @see #first * @return the first element. */ public A _1() { return this.first; } /** * Scala-style getter for {@link #second}. * * @see #second * @return the second element. */ public B _2() { return this.second; } /** * Applies the given function to both elements of this pair. * * @see #uncurry(BiFunction) * @param * return type. * @param f * A function on two elements. * @throws NullPointerException * if f is null * @return The result of applying this pair to f. */ public R applyTo(final BiFunction f) { return f.apply(this.first, this.second); } @Override public int hashCode() { return Objects.hash(this.first, this.second); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Pair)) return false; final Pair other = (Pair) obj; return Objects.equals(this.first, other.first) && Objects.equals(this.second, other.second); } /** * Creates an inverted pair. *

* (a, b) → (b, a) * * @return new Pair<>(this.second, this.first) */ public Pair swap() { return new Pair<>(this.second, this.first); } /** * This Pair as an array so that first is on index 0 and second is on index 1. * * @return new Object[] { this.first, this.second }; */ public Object[] toArray() { return new Object[] { this.first, this.second }; } /** * Returns a string representation of this Pair. * * @return "Pair(first, second)" */ @Override public String toString() { return "Pair(" + this.first + ", " + this.second + ")"; } } public static Stream zip(Stream a, Stream b, BiFunction zipper) { Objects.requireNonNull(zipper); @SuppressWarnings("unchecked") Spliterator as = (Spliterator) Objects.requireNonNull(a).spliterator(); @SuppressWarnings("unchecked") Spliterator bs = (Spliterator) Objects.requireNonNull(b).spliterator(); // Combining loses DISTINCT and SORTED characteristics and for other // characteristics the combined stream has a characteristic if both // streams to combine have the characteristic int characteristics = as.characteristics() & bs.characteristics() & ~(Spliterator.DISTINCT | Spliterator.SORTED); long size = Math.min(as.estimateSize(), bs.estimateSize()); Spliterator cs = new ZipperSpliterator<>(as, bs, zipper, size, characteristics); return StreamSupport.stream(cs, a.isParallel() || b.isParallel()); } final static Object NONE = new Object(); private static final class ZipperSpliterator extends java.util.Spliterators.AbstractSpliterator implements Consumer { final Spliterator as; final Spliterator bs; final BiFunction zipper; Object a; Object b; ZipperSpliterator(Spliterator as, Spliterator bs, BiFunction zipper, long est, int additionalCharacteristics) { super(est, additionalCharacteristics); this.as = as; this.bs = bs; this.zipper = zipper; this.a = NONE; } @Override public void accept(Object aOrB) { if (this.a == NONE) { this.a = aOrB; } else { this.b = aOrB; } } @Override @SuppressWarnings("unchecked") public boolean tryAdvance(Consumer action) { if (this.as.tryAdvance(this) && this.bs.tryAdvance(this)) { Object _a = this.a; this.a = NONE; action.accept(this.zipper.apply((A) _a, (B) this.b)); return true; } return false; } } }