// 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.
*
* <p>
* This was removed later and no zip function exists in the final Java 8 release: <a
* href="http://hg.openjdk.java.net/lambda/lambda/jdk/rev/569088178418">changeset
* 8789:569088178418.</a>
* <p>
* <a href="http://stackoverflow.com/questions/17640754">Discussion on Stackoverflow.</a>
*
*/
public final class StreamZipper {
/** A very simple Pair that can be used to zip two streams.
*
* @author Claude Martin */
public static final class Pair<A, B> {
/** 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 <R>
* 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> R applyTo(final BiFunction<? super A, ? super B, R> 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.
* <p>
* <code>(a, b) → (b, a)</code>
*
* @return <code>new Pair<>(this.second, this.first)</code>
*/
public Pair<B, A> 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 <code>new Object[] { this.first, this.second };</code>
*/
public Object[] toArray() {
return new Object[] { this.first, this.second };
}
/**
* Returns a string representation of this Pair.
*
* @return "Pair(<i>first</i>, <i>second</i>)"
*/
@Override
public String toString() {
return "Pair(" + this.first + ", " + this.second + ")";
}
}
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);
@SuppressWarnings("unchecked")
Spliterator<A> as = (Spliterator<A>) Objects.requireNonNull(a).spliterator();
@SuppressWarnings("unchecked")
Spliterator<B> bs = (Spliterator<B>) 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<C> 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<A, B, C> extends
java.util.Spliterators.AbstractSpliterator<C> implements Consumer<Object> {
final Spliterator<A> as;
final Spliterator<B> bs;
final BiFunction<? super A, ? super B, ? extends C> zipper;
Object a;
Object b;
ZipperSpliterator(Spliterator<A> as, Spliterator<B> bs,
BiFunction<? super A, ? super B, ? extends C> 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<? super C> 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;
}
}
}
Comments