cyclops.companion.Streams Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cyclops Show documentation
Show all versions of cyclops Show documentation
Platform for Functional Reactive Programming with Java 8
package cyclops.companion;
import com.oath.cyclops.internal.stream.*;
import com.oath.cyclops.internal.stream.operators.DebounceOperator;
import com.oath.cyclops.internal.stream.operators.MultiReduceOperator;
import com.oath.cyclops.internal.stream.operators.OnePerOperator;
import com.oath.cyclops.internal.stream.operators.RecoverOperator;
import com.oath.cyclops.internal.stream.spliterators.*;
import com.oath.cyclops.types.persistent.PersistentCollection;
import com.oath.cyclops.types.stream.Connectable;
import com.oath.cyclops.types.stream.NonPausableConnectable;
import com.oath.cyclops.types.traversable.Traversable;
import cyclops.control.Eval;
import cyclops.control.Maybe;
import cyclops.control.Option;
import cyclops.data.Seq;
import cyclops.control.Either;
import cyclops.data.Vector;
import cyclops.function.*;
import cyclops.reactive.ReactiveSeq;
import com.oath.cyclops.util.box.Mutable;
import com.oath.cyclops.types.stream.PausableConnectable;
import com.oath.cyclops.util.ExceptionSoftener;
import lombok.AllArgsConstructor;
import lombok.experimental.UtilityClass;
import lombok.val;
import cyclops.data.tuple.Tuple;
import cyclops.data.tuple.Tuple2;
import cyclops.data.tuple.Tuple3;
import cyclops.data.tuple.Tuple4;
import com.oath.cyclops.types.persistent.PersistentList;
import org.reactivestreams.Subscription;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import java.util.function.*;
import java.util.stream.*;
/**
* Static utility methods for working with Java 8 Streams
*
* @author johnmcclean
*
*/
@UtilityClass
public class Streams {
public static ReactiveSeq> combinations(int size,Object[] a){
final int fromIndex = 0;
final int toIndex = a.length;
final Iterator> iter = new Iterator>() {
private final int[] indices = IntStream.range(fromIndex, fromIndex + size).toArray();
@Override
public boolean hasNext() {
return indices[0] <= toIndex - size;
}
@Override
public ReactiveSeq next() {
final List result = new ArrayList<>(size);
for (int idx : indices) {
result.add((T)a[idx]);
}
if (++indices[size - 1] == toIndex) {
for (int i = size - 1; i > 0; i--) {
if (indices[i] > toIndex - (size - i)) {
indices[i - 1]++;
for (int j = i; j < size; j++) {
indices[j] = indices[j - 1] + 1;
}
}
}
}
return ReactiveSeq.fromList(result);
}
};
return ReactiveSeq.fromIterator(iter);
}
public static ReactiveSeq> permutations(Object[] a){
final Iterator> iter = new Iterator>() {
private final int[] indices = IntStream.range(0, a.length).toArray();
private Option> next = Maybe.fromEval(Eval.later(this::first)).flatMap(i->i);
@Override
public boolean hasNext() {
return next.isPresent();
}
@Override
public ReactiveSeq next() {
ReactiveSeq res = next.orElse(null);
next = Maybe.fromEval(Eval.later(this::genNext)).flatMap(i->i);
return res;
}
private Option> first(){
return indices.length>0 ? Option.some(ReactiveSeq.fromList(buildList())) : Option.none();
}
private Option> genNext() {
return gen(findNextIndexByOrder());
}
private Option> gen(int next) {
return next<0 ? Option.none() : Option.some(ReactiveSeq.fromList(buildAndSwap(next)));
}
private void swapIndices(int i) {
swap(i++,findMinIndex(i-1));
for (int j = indices.length - 1 ;i < j;i++,j--) {
swap(i,j);
}
}
private int findNextIndexByOrder(){
int i = indices.length - 2;
for (;i >= 0 && indices[i] > indices[i + 1];--i) {
}
return i;
}
private int findMinIndex(int startIdx){
int idx = startIdx + 1;
int currentMinValue = indices[idx];
int minIndex = idx;
for(;idx buildAndSwap(int next) {
swapIndices(next);
return buildList();
}
private List buildList() {
final List result = new ArrayList<>(indices.length);
for (int idx : indices) {
result.add((T)a[idx]);
}
return result;
}
private void swap(int a, int b) {
int temp = indices[a];
indices[a] = indices[b];
indices[b] = temp;
}
};
return ReactiveSeq.fromIterator(iter);
}
/**
* Perform a For Comprehension over a Stream, accepting 3 generating arrow.
* This results in a four level nested internal iteration over the provided Publishers.
*
*
* {@code
*
* import static cyclops.companion.Streams.forEach4;
*
forEach4(IntStream.range(1,10).boxed(),
a-> Stream.iterate(a,i->i+1).limit(10),
(a,b) -> Stream.of(a+b),
(a,b,c) -> Stream.just(a+b+c),
Tuple::tuple)
*
* }
*
*
* @param value1 top level Stream
* @param value2 Nested Stream
* @param value3 Nested Stream
* @param value4 Nested Stream
* @param yieldingFunction Generates a result per combination
* @return Stream with an element per combination of nested publishers generated by the yielding function
*/
public static Stream forEach4(Stream value1,
Function> value2,
BiFunction> value3,
Function3> value4,
Function4 yieldingFunction) {
return value1.flatMap(in -> {
Stream a = value2.apply(in);
return a.flatMap(ina -> {
Stream b = value3.apply(in,ina);
return b.flatMap(inb -> {
Stream c = value4.apply(in,ina,inb);
return c.map(in2 -> yieldingFunction.apply(in, ina, inb, in2));
});
});
});
}
/**
* Perform a For Comprehension over a Stream, accepting 3 generating function.
* This results in a four level nested internal iteration over the provided Publishers.
*
* {@code
*
* import static com.oath.cyclops.reactor.Streames.forEach4;
*
* forEach4(IntStream.range(1,10).boxed(),
a-> Stream.iterate(a,i->i+1).limit(10),
(a,b) -> Stream.just(a+b),
(a,b,c) -> Stream.just(a+b+c),
(a,b,c,d) -> a+b+c+d <100,
Tuple::tuple);
*
* }
*
*
* @param value1 top level Stream
* @param value2 Nested Stream
* @param value3 Nested Stream
* @param value4 Nested Stream
* @param filterFunction A filtering function, keeps values where the predicate holds
* @param yieldingFunction Generates a result per combination
* @return Stream with an element per combination of nested publishers generated by the yielding function
*/
public static Stream forEach4(Stream value1,
Function> value2,
BiFunction> value3,
Function3> value4,
Function4 filterFunction,
Function4 yieldingFunction) {
return value1.flatMap(in -> {
Stream a = value2.apply(in);
return a.flatMap(ina -> {
Stream b = value3.apply(in,ina);
return b.flatMap(inb -> {
Stream c = value4.apply(in,ina,inb);
return c.filter(in2->filterFunction.apply(in,ina,inb,in2))
.map(in2 -> yieldingFunction.apply(in, ina, inb, in2));
});
});
});
}
/**
* Perform a For Comprehension over a Stream, accepting 2 generating function.
* This results in a three level nested internal iteration over the provided Publishers.
*
*
* {@code
*
* import static Streams.forEach3;
*
* forEach(IntStream.range(1,10).boxed(),
a-> Stream.iterate(a,i->i+1).limit(10),
(a,b) -> Stream.of(a+b),
Tuple::tuple);
*
* }
*
*
*
* @param value1 top level Stream
* @param value2 Nested Stream
* @param value3 Nested Stream
* @param yieldingFunction Generates a result per combination
* @return Stream with an element per combination of nested publishers generated by the yielding function
*/
public static Stream forEach3(Stream value1,
Function> value2,
BiFunction> value3,
Function3 yieldingFunction) {
return value1.flatMap(in -> {
Stream a = value2.apply(in);
return a.flatMap(ina -> {
Stream b = value3.apply(in,ina);
return b.map(in2 -> yieldingFunction.apply(in, ina, in2));
});
});
}
/**
* Perform a For Comprehension over a Stream, accepting 2 generating function.
* This results in a three level nested internal iteration over the provided Publishers.
*
* {@code
*
* import static Streams.forEach;
*
* forEach(IntStream.range(1,10).boxed(),
a-> Stream.iterate(a,i->i+1).limit(10),
(a,b) -> Stream.of(a+b),
(a,b,c) ->a+b+c<10,
Tuple::tuple)
.listX();
* }
*
*
* @param value1 top level Stream
* @param value2 Nested publisher
* @param value3 Nested publisher
* @param filterFunction A filtering function, keeps values where the predicate holds
* @param yieldingFunction Generates a result per combination
* @return
*/
public static Stream forEach3(Stream value1,
Function> value2,
BiFunction> value3,
Function3 filterFunction,
Function3 yieldingFunction) {
return value1.flatMap(in -> {
Stream a = value2.apply(in);
return a.flatMap(ina -> {
Stream b = value3.apply(in,ina);
return b.filter(in2->filterFunction.apply(in,ina,in2))
.map(in2 -> yieldingFunction.apply(in, ina, in2));
});
});
}
/**
* Perform a For Comprehension over a Stream, accepting an additonal generating function.
* This results in a two level nested internal iteration over the provided Publishers.
*
*
* {@code
*
* import static Streams.forEach2;
* forEach(IntStream.range(1, 10).boxed(),
* i -> Stream.range(i, 10), Tuple::tuple)
.forEach(System.out::println);
//(1, 1)
(1, 2)
(1, 3)
(1, 4)
...
*
* }
*
* @param value1 top level Stream
* @param value2 Nested publisher
* @param yieldingFunction Generates a result per combination
* @return
*/
public static Stream forEach2(Stream value1,
Function> value2,
BiFunction yieldingFunction) {
return value1.flatMap(in -> {
Stream a = value2.apply(in);
return a.map(in2 -> yieldingFunction.apply(in, in2));
});
}
/**
*
*
* {@code
*
* import static Streams.forEach2;
*
* forEach(IntStream.range(1, 10).boxed(),
* i -> Stream.range(i, 10),
* (a,b) -> a>2 && b<10,
* Tuple::tuple)
.forEach(System.out::println);
//(3, 3)
(3, 4)
(3, 5)
(3, 6)
(3, 7)
(3, 8)
(3, 9)
...
*
* }
*
*
* @param value1 top level Stream
* @param value2 Nested publisher
* @param filterFunction A filtering function, keeps values where the predicate holds
* @param yieldingFunction Generates a result per combination
* @return
*/
public static Stream forEach2(Stream value1,
Function> value2,
BiFunction filterFunction,
BiFunction yieldingFunction) {
return value1.flatMap(in -> {
Stream a = value2.apply(in);
return a.filter(in2->filterFunction.apply(in,in2))
.map(in2 -> yieldingFunction.apply(in, in2));
});
}
/**
* Create an Optional containing a List materialized from a Stream
*
*
* {@code
* Optional> opt = Streams.streamToOptional(Stream.of(1,2,3));
*
* //Optional[[1,2,3]]
*
* }
*
*
*
* @param stream To convert into an Optional
* @return Optional with a List of values
*/
public final static Optional> streamToOptional(final Stream stream) {
final List collected = stream.collect(java.util.stream.Collectors.toList());
if (collected.size() == 0)
return Optional.empty();
return Optional.of(Seq.fromIterable(collected));
}
/**
* Convert an Optional to a Stream
*
*
* {@code
* Stream stream = Streams.optionalToStream(Optional.of(1));
* //Stream[1]
*
* Stream zero = Streams.optionalToStream(Optional.zero());
* //Stream[]
* }
*
*
* @param optional Optional to convert to a Stream
* @return Stream with a single value (if present) created from an Optional
*/
public final static Stream optionalToStream(final Optional optional) {
if (optional.isPresent())
return Stream.of(optional.get());
return Stream.of();
}
/**
* Create a CompletableFuture containing a List materialized from a Stream
*
* @param stream To convert into an Optional
* @return CompletableFuture with a List of values
*/
public final static CompletableFuture> streamToCompletableFuture(final Stream stream) {
return CompletableFuture.completedFuture(stream.collect(Collectors.toList()));
}
/**
* Convert a CompletableFuture to a Stream
*
* @param future CompletableFuture to convert
* @return Stream with a single value created from a CompletableFuture
*/
public final static Stream completableFutureToStream(final CompletableFuture future) {
return Stream.of(future.join());
}
/**
* Perform a forEach operation over the Stream, without closing it, consuming only the specified number of elements from
* the Stream, at this time. More elements can be consumed later, by called request on the returned Subscription
*
*
* @{code
* Subscription next = Streams.forEach(Stream.of(1,2,3,4),2,System.out::println);
*
* System.out.println("First batch processed!");
*
* next.request(2);
*
* System.out.println("Second batch processed!");
*
* //prints
* 1
* 2
* First batch processed!
* 3
* 4
* Second batch processed!
* }
*
*
* @param stream - the Stream to consume data from
* @param x To consume from the Stream at this time
* @param consumerElement To accept incoming events from the Stream
* @return Subscription so that further processing can be continued or cancelled.
*/
public static Subscription forEach(final Stream stream, final long x, final Consumer consumerElement) {
val t2 = FutureStreamUtils.forEachX(stream, x, consumerElement);
t2._2().run();
return t2._1().join();
}
/**
* Perform a forEach operation over the Stream without closing it, capturing any elements and errors in the supplied consumers, but only consuming
* the specified number of elements from the Stream, at this time. More elements can be consumed later, by called request on the returned Subscription
*
* @{code
* Subscription next = Streams.forEach(Stream.of(()->1,()->2,()->{throw new RuntimeException()},()->4)
* .map(Supplier::getValue),System.out::println, e->e.printStackTrace());
*
* System.out.println("First batch processed!");
*
* next.request(2);
*
* System.out.println("Second batch processed!");
*
* //prints
* 1
* 2
* First batch processed!
*
* RuntimeException Stack Trace on System.err
*
* 4
* Second batch processed!
* }
*
*
* @param stream - the Stream to consume data from
* @param x To consume from the Stream at this time
* @param consumerElement To accept incoming elements from the Stream
* @param consumerError To accept incoming processing errors from the Stream
* @return Subscription so that further processing can be continued or cancelled.
*/
public static Subscription forEach(final Stream stream, final long x,
final Consumer consumerElement, final Consumer consumerError) {
val t2 = FutureStreamUtils.forEachXWithError(stream, x, consumerElement, consumerError);
t2._2().run();
return t2._1().join();
}
/**
* Perform a forEach operation over the Stream without closing it, capturing any elements and errors in the supplied consumers, but only consuming
* the specified number of elements from the Stream, at this time. More elements can be consumed later, by called request on the returned Subscription,
* when the entire Stream has been processed an onComplete event will be recieved.
*
*
* @{code
* Subscription next = Streams.forEach(Stream.of(()->1,()->2,()->{throw new RuntimeException()},()->4)
* .map(Supplier::getValue) ,System.out::println, e->e.printStackTrace(),()->System.out.println("the take!"));
*
* System.out.println("First batch processed!");
*
* next.request(2);
*
* System.out.println("Second batch processed!");
*
* //prints
* 1
* 2
* First batch processed!
*
* RuntimeException Stack Trace on System.err
*
* 4
* Second batch processed!
* The take!
* }
*
* @param stream - the Stream to consume data from
* @param x To consume from the Stream at this time
* @param consumerElement To accept incoming elements from the Stream
* @param consumerError To accept incoming processing errors from the Stream
* @param onComplete To run after an onComplete event
* @return Subscription so that further processing can be continued or cancelled.
*/
public static Subscription forEach(final Stream stream, final long x,
final Consumer consumerElement, final Consumer consumerError, final Runnable onComplete) {
val t2 = FutureStreamUtils.forEachXEvents(stream, x, consumerElement, consumerError, onComplete);
t2._2().run();
return t2._1().join();
}
/**
* Perform a forEach operation over the Stream capturing any elements and errors in the supplied consumers,
*
* @{code
* Subscription next = Streams.forEach(Stream.of(()->1,()->2,()->{throw new RuntimeException()},()->4)
* .map(Supplier::getValue),System.out::println, e->e.printStackTrace());
*
* System.out.println("processed!");
*
*
*
* //prints
* 1
* 2
* RuntimeException Stack Trace on System.err
* 4
* processed!
*
* }
*
* @param stream - the Stream to consume data from
* @param consumerElement To accept incoming elements from the Stream
* @param consumerError To accept incoming processing errors from the Stream
*/
public static void forEach(final Stream stream, final Consumer consumerElement,
final Consumer consumerError) {
val t2 = FutureStreamUtils.forEachWithError(stream, consumerElement, consumerError);
t2._2().run();
}
/**
* Perform a forEach operation over the Stream capturing any elements and errors in the supplied consumers
* when the entire Stream has been processed an onComplete event will be recieved.
*
*
* @{code
* Subscription next = Streams.forEach(Stream.of(()->1,()->2,()->{throw new RuntimeException()},()->4)
* .map(Supplier::getValue),System.out::println, e->e.printStackTrace(),()->System.out.println("the take!"));
*
* System.out.println("processed!");
*
*
* //prints
* 1
* 2
* RuntimeException Stack Trace on System.err
* 4
* processed!
*
*
* }
*
* @param stream - the Stream to consume data from
* @param consumerElement To accept incoming elements from the Stream
* @param consumerError To accept incoming processing errors from the Stream
* @param onComplete To run after an onComplete event
* @return Subscription so that further processing can be continued or cancelled.
*/
public static void forEach(final Stream stream, final Consumer consumerElement,
final Consumer consumerError, final Runnable onComplete) {
val t2 = FutureStreamUtils.forEachEvent(stream, consumerElement, consumerError, onComplete);
t2._2().run();
}
/**
* Execute this Stream on a schedule
*
*
* {@code
* //run at 8PM every night
* Streams.schedule(Stream.generate(()->"next job:"+formatDate(new Date()))
* .map(this::processJob)
* ,"0 20 * * *",Executors.newScheduledThreadPool(1)));
* }
*
*
* Connect to the Scheduled Stream
*
*
* {@code
* Connectable dataStream = Streams.schedule(Stream.generate(()->"next job:"+formatDate(new Date()))
* .map(this::processJob)
* ,"0 20 * * *",Executors.newScheduledThreadPool(1)));
*
*
* data.connect().forEach(this::logToDB);
* }
*
*
*
* @param stream the stream to schedule element processing on
* @param cron Expression that determines when each job will run
* @param ex ScheduledExecutorService
* @return Connectable Connectable of emitted from scheduled Stream
*/
public static Connectable schedule(final Stream stream, final String cron, final ScheduledExecutorService ex) {
return new NonPausableConnectable<>(
stream).schedule(cron, ex);
}
/**
* Execute this Stream on a schedule
*
*
* {@code
* //run every 60 seconds after last job completes
* Streams.scheduleFixedDelay(Stream.generate(()->"next job:"+formatDate(new Date()))
* .map(this::processJob)
* ,60_000,Executors.newScheduledThreadPool(1)));
* }
*
*
* Connect to the Scheduled Stream
*
*
* {@code
* Connectable dataStream = Streams.scheduleFixedDelay(Stream.generate(()->"next job:"+formatDate(new Date()))
* .map(this::processJob)
* ,60_000,Executors.newScheduledThreadPool(1)));
*
*
* data.connect().forEach(this::logToDB);
* }
*
*
*
* @param stream the stream to schedule element processing on
* @param delay Between last element completes passing through the Stream until the next one starts
* @param ex ScheduledExecutorService
* @return Connectable Connectable of emitted from scheduled Stream
*/
public static Connectable scheduleFixedDelay(final Stream stream, final long delay, final ScheduledExecutorService ex) {
return new NonPausableConnectable<>(
stream).scheduleFixedDelay(delay, ex);
}
/**
* Execute this Stream on a schedule
*
*
* {@code
* //run every 60 seconds
* Streams.scheduleFixedRate(Stream.generate(()->"next job:"+formatDate(new Date()))
* .map(this::processJob),
* 60_000,Executors.newScheduledThreadPool(1)));
* }
*
*
* Connect to the Scheduled Stream
*
*
* {@code
* Connectable dataStream = Streams.scheduleFixedRate(Stream.generate(()->"next job:"+formatDate(new Date()))
* .map(this::processJob)
* ,60_000,Executors.newScheduledThreadPool(1)));
*
*
* data.connect().forEach(this::logToDB);
* }
*
* @param stream the stream to schedule element processing on
* @param rate Time in millis between job runs
* @param ex ScheduledExecutorService
* @return Connectable Connectable of emitted from scheduled Stream
*/
public static Connectable scheduleFixedRate(final Stream stream, final long rate, final ScheduledExecutorService ex) {
return new NonPausableConnectable<>(
stream).scheduleFixedRate(rate, ex);
}
/**
* Split at supplied location
*
* {@code
* ReactiveSeq.of(1,2,3).splitAt(1)
*
* //Stream[1], Stream[2,3]
* }
*
*
*/
public final static Tuple2, Stream> splitAt(final Stream stream, final int where) {
final Tuple2, Stream> Tuple2 = duplicate(stream);
return Tuple.tuple(
Tuple2._1().limit(where), Tuple2._2().skip(where));
}
/**
* Split stream at point where predicate no longer holds
*
* {@code
* ReactiveSeq.of(1, 2, 3, 4, 5, 6).splitBy(i->i<4)
*
* //Stream[1,2,3] Stream[4,5,6]
* }
*
*/
public final static Tuple2, Stream> splitBy(final Stream stream, final Predicate splitter) {
final Tuple2, Stream> Tuple2 = duplicate(stream);
return Tuple.tuple(
takeWhile(Tuple2._1(), splitter), dropWhile(Tuple2._2(), splitter));
}
/**
* Partition a Stream into two one a per element basis, based on predicate's boolean value
*
* {@code
* ReactiveSeq.of(1, 2, 3, 4, 5, 6).partition(i -> i % 2 != 0)
*
* //Stream[1,3,5], Stream[2,4,6]
* }
*
*
*/
public final static Tuple2, Stream> partition(final Stream stream, final Predicate splitter) {
final Tuple2, Stream> Tuple2 = duplicate(stream);
return Tuple.tuple(
Tuple2._1().filter(splitter), Tuple2._2().filter(splitter.negate()));
}
/**
* Duplicate a Stream, buffers intermediate values, leaders may change positions so a limit
* can be safely applied to the leading stream. Not thread-safe.
*
* {@code
* Tuple2, ReactiveSeq> copies =of(1,2,3,4,5,6).duplicate();
assertTrue(copies._1.anyMatch(i->i==2));
assertTrue(copies._2.anyMatch(i->i==2));
*
* }
*
*
* @return duplicated stream
*/
public final static Tuple2, Stream> duplicate(final Stream stream) {
final Tuple2, Iterator> Tuple2 = Streams.toBufferingDuplicator(stream.iterator());
return Tuple.tuple(
Streams.stream(Tuple2._1()), Streams.stream(Tuple2._2()));
}
/**
* Duplicate a Stream, buffers intermediate values, leaders may change positions so a limit
* can be safely applied to the leading stream. Not thread-safe.
*
* {@code
* Tuple2, ReactiveSeq> copies =of(1,2,3,4,5,6).duplicate();
assertTrue(copies._1.anyMatch(i->i==2));
assertTrue(copies._2.anyMatch(i->i==2));
*
* }
*
*
* @return duplicated stream
*/
public final static Tuple2, Stream> duplicate(final Stream stream,Supplier> bufferFactory) {
final Tuple2, Iterator> Tuple2 = Streams.toBufferingDuplicator(stream.iterator(),bufferFactory);
return Tuple.tuple(
Streams.stream(Tuple2._1()), Streams.stream(Tuple2._2()));
}
private final static Tuple2, Stream> duplicatePos(final Stream stream, final int pos) {
final Tuple2, Iterator> Tuple2 = Streams.toBufferingDuplicator(stream.iterator(), pos);
return Tuple.tuple(
Streams.stream(Tuple2._1()), Streams.stream(Tuple2._2()));
}
/**
* Triplicates a Stream
* Buffers intermediate values, leaders may change positions so a limit
* can be safely applied to the leading stream. Not thread-safe.
*
* {@code
* Tuple3>,ReactiveSeq>,ReactiveSeq>> Tuple3 = sequence.triplicate();
* }
*
*/
@SuppressWarnings("unchecked")
public final static Tuple3, Stream, Stream> triplicate(final Stream stream) {
final Stream> its = Streams.toBufferingCopier(stream.iterator(), 3)
.stream()
.map(it -> Streams.stream(it));
final Iterator> it = its.iterator();
return new Tuple3(
it.next(), it.next(), it.next());
}
/**
* Triplicates a Stream
* Buffers intermediate values, leaders may change positions so a limit
* can be safely applied to the leading stream. Not thread-safe.
*
* {@code
* Tuple3>,ReactiveSeq>,ReactiveSeq>> Tuple3 = sequence.triplicate();
* }
*
*/
@SuppressWarnings("unchecked")
public final static Tuple3, Stream, Stream> triplicate(final Stream stream, Supplier> bufferFactory) {
final Stream> its = Streams.toBufferingCopier(stream.iterator(), 3,bufferFactory)
.stream()
.map(it -> Streams.stream(it));
final Iterator> it = its.iterator();
return new Tuple3(
it.next(), it.next(), it.next());
}
/**
* Makes four copies of a Stream
* Buffers intermediate values, leaders may change positions so a limit
* can be safely applied to the leading stream. Not thread-safe.
*
*
* {@code
*
* Tuple4>,ReactiveSeq>,ReactiveSeq>,ReactiveSeq>> quad = sequence.quadruplicate();
* }
*
* @return
*/
@SuppressWarnings("unchecked")
public final static Tuple4, Stream, Stream, Stream> quadruplicate(final Stream stream) {
final Stream> its = Streams.toBufferingCopier(stream.iterator(), 4)
.stream()
.map(it -> Streams.stream(it));
final Iterator> it = its.iterator();
return new Tuple4(
it.next(), it.next(), it.next(), it.next());
}
/**
* Makes four copies of a Stream
* Buffers intermediate values, leaders may change positions so a limit
* can be safely applied to the leading stream. Not thread-safe.
*
*
* {@code
*
* Tuple4>,ReactiveSeq>,ReactiveSeq>,ReactiveSeq>> quad = sequence.quadruplicate();
* }
*
* @return
*/
@SuppressWarnings("unchecked")
public final static Tuple4, Stream, Stream, Stream> quadruplicate(final Stream stream, Supplier> bufferFactory) {
final Stream> its = Streams.toBufferingCopier(stream.iterator(), 4,bufferFactory)
.stream()
.map(it -> Streams.stream(it));
final Iterator> it = its.iterator();
return new Tuple4(
it.next(), it.next(), it.next(), it.next());
}
/**
* Append Stream to this Stream
*
*
* {@code
* List result = of(1,2,3).appendStream(of(100,200,300))
.map(it ->it+"!!")
.collect(CyclopsCollectors.toList());
assertThat(result,equalTo(Arrays.asList("1!!","2!!","3!!","100!!","200!!","300!!")));
* }
*
*
* @param stream1 to append to
* @param append to append with
* @return Stream with Stream appended
*/
public static final Stream appendStream(final Stream stream1, final Stream append) {
return Stream.concat(stream1, append);
}
/**
* Prepend Stream to this Stream
*
*
* {@code
* List result = of(1,2,3).prependStream(of(100,200,300))
.map(it ->it+"!!").collect(CyclopsCollectors.toList());
assertThat(result,equalTo(Arrays.asList("100!!","200!!","300!!","1!!","2!!","3!!")));
*
* }
*
*
* @param stream1 to Prepend to
* @param prepend to Prepend with
* @return Stream with Stream prepended
*/
public static final Stream prependStream(final Stream stream1, final Stream prepend) {
return Stream.concat(prepend, stream1);
}
/**
* Append values to the take of this Stream
*
* {@code
* List result = of(1,2,3).append(100,200,300)
.map(it ->it+"!!")
.collect(CyclopsCollectors.toList());
assertThat(result,equalTo(Arrays.asList("1!!","2!!","3!!","100!!","200!!","300!!")));
* }
*
* @param values to append
* @return Stream with appended values
*/
public static final Stream append(final Stream stream, final T... values) {
return appendStream(stream, Stream.of(values));
}
/**
* Prepend given values to the skip of the Stream
*
* {@code
* List result = of(1,2,3).prependAll(100,200,300)
.map(it ->it+"!!").collect(CyclopsCollectors.toList());
assertThat(result,equalTo(Arrays.asList("100!!","200!!","300!!","1!!","2!!","3!!")));
* }
* @param values to prependAll
* @return Stream with values prepended
*/
public static final Stream prepend(final Stream stream, final T... values) {
return appendStream(Stream.of(values), stream);
}
/**
* Insert data into a stream at given position
*
* {@code
* List result = of(1,2,3).insertAt(1,100,200,300)
.map(it ->it+"!!").collect(CyclopsCollectors.toList());
assertThat(result,equalTo(Arrays.asList("1!!","100!!","200!!","300!!","2!!","3!!")));
*
* }
*
* @param pos to insert data at
* @param values to insert
* @return Stream with new data inserted
*/
public static final Stream insertAt(final Stream stream, final int pos, final T... values) {
final Tuple2, Stream> Tuple2 = duplicatePos(stream, pos);
return appendStream(append(Tuple2._1().limit(pos), values), Tuple2._2().skip(pos));
}
/**
* Delete elements between given indexes in a Stream
*
* {@code
* List result = Streams.deleteBetween(Stream.of(1,2,3,4,5,6),2,4)
.map(it ->it+"!!")
.collect(CyclopsCollectors.toList());
assertThat(result,equalTo(Arrays.asList("1!!","2!!","5!!","6!!")));
* }
*
* @param start index
* @param end index
* @return Stream with elements removed
*/
public static final Stream deleteBetween(final Stream stream, final int start, final int end) {
final Tuple2, Stream> Tuple2 = duplicatePos(stream, start);
return appendStream(Tuple2._1().limit(start), Tuple2._2().skip(end));
}
/**
* Insert a Stream into the middle of this stream at the specified position
*
* {@code
* List result = Streams.insertAt(Stream.of(1,2,3),1,of(100,200,300))
.map(it ->it+"!!")
.collect(CyclopsCollectors.toList());
assertThat(result,equalTo(Arrays.asList("1!!","100!!","200!!","300!!","2!!","3!!")));
* }
*
* @param stream1 to insert in
* @param pos to insert Stream at
* @param insert to insert
* @return newly conjoined Stream
*/
public static final Stream insertStreamAt(final Stream stream1, final int pos, final Stream insert) {
final Tuple2, Stream> Tuple2 = duplicatePos(stream1, pos);
return appendStream(appendStream(Tuple2._1().limit(pos), insert), Tuple2._2().skip(pos));
}
/**
* skip elements in Stream until Predicate holds true
*
* {@code Streams.dropUntil(Stream.of(4,3,6,7),i->i==6).collect(CyclopsCollectors.toList())
* // [6,7]
* }
* @param stream Stream to skip elements from
* @param predicate to applyHKT
* @return Stream with elements skipped
*/
public static Stream dropUntil(final Stream stream, final Predicate predicate) {
return dropWhile(stream, predicate.negate());
}
public static Stream dropRight(final Stream stream, final int num) {
return StreamSupport.stream(SkipLastSpliterator.dropRight(stream.spliterator(),num),stream.isParallel());
}
public static Stream takeRight(final Stream stream, final int num) {
return StreamSupport.stream(LimitLastSpliterator.takeRight(stream.spliterator(), num),stream.isParallel());
}
public static Stream recover(final Stream stream, final Function fn) {
return new RecoverOperator<>(
stream, Throwable.class).recover(fn);
}
public static Stream recover(final Stream stream, final Class type, final Function fn) {
return new RecoverOperator(
stream, (Class) type).recover((Function) fn);
}
/**
* skip elements in a Stream while Predicate holds true
*
*
*
* {@code Streams.dropWhile(Stream.of(4,3,6,7).sorted(),i->i<6).collect(CyclopsCollectors.toList())
* // [6,7]
* }
* @param stream
* @param predicate
* @return
*/
public static Stream dropWhile(final Stream stream, final Predicate predicate) {
return StreamSupport.stream(new SkipWhileSpliterator(stream.spliterator(),predicate), stream.isParallel());
}
public static Stream take(final Stream stream, final long time, final TimeUnit unit) {
return StreamSupport.stream(
new LimitWhileTimeSpliterator(stream.spliterator(),time,unit),stream.isParallel());
}
public static Stream drop(final Stream stream, final long time, final TimeUnit unit) {
return StreamSupport.stream(new SkipWhileTimeSpliterator(stream.spliterator(),time,unit),stream.isParallel());
}
public static Stream combine(final Stream stream, final BiPredicate predicate, final BinaryOperator op) {
final Iterator it = stream.iterator();
final Object UNSET = new Object();
return Streams.stream(new Iterator>() {
T current = (T) UNSET;
@Override
public boolean hasNext() {
return it.hasNext() || current != UNSET;
}
@Override
public ReactiveSeq next() {
while (it.hasNext()) {
final T next = it.next();
if (current == UNSET) {
current = next;
} else if (predicate.test(current, next)) {
current = op.apply(current, next);
} else {
final T result = current;
current = (T) UNSET;
return ReactiveSeq.of(result, next);
}
}
if (it.hasNext())
return ReactiveSeq.empty();
final T result = current;
current = (T) UNSET;
return ReactiveSeq.of(result);
}
})
.flatMap(Function.identity());
}
public static Iterable> combineI(final Iterable stream, final BiPredicate predicate, final BinaryOperator op) {
final Object UNSET = new Object();
return ()-> new Iterator>() {
T current = (T) UNSET;
final Iterator it = stream.iterator();
@Override
public boolean hasNext() {
return it.hasNext() || current != UNSET;
}
@Override
public ReactiveSeq next() {
while (it.hasNext()) {
final T next = it.next();
if (current == UNSET) {
current = next;
} else if (predicate.test(current, next)) {
current = op.apply(current, next);
} else {
final T result = current;
current = (T) UNSET;
return ReactiveSeq.of(result, next);
}
}
if (it.hasNext())
return ReactiveSeq.empty();
final T result = current;
current = (T) UNSET;
return ReactiveSeq.of(result);
}
};
}
/**
* Take elements from a stream while the predicates hold
*
* {@code Streams.takeWhile(Stream.of(4,3,6,7).sorted(),i->i<6).collect(CyclopsCollectors.toList());
* //[4,3]
* }
*
* @param stream
* @param predicate
* @return
*/
public static Stream takeWhile(final Stream stream, final Predicate predicate) {
return StreamSupport.stream(new LimitWhileSpliterator<>(stream.spliterator(), predicate),stream.isParallel());
}
/**
* Take elements from a Stream until the predicate holds
*
* {@code Streams.takeUntil(Stream.of(4,3,6,7),i->i==6).collect(CyclopsCollectors.toList());
* //[4,3]
* }
*
* @param stream
* @param predicate
* @return
*/
public static Stream takeUntil(final Stream stream, final Predicate predicate) {
return takeWhile(stream, predicate.negate());
}
/**
* Reverse a Stream
*
*
* {@code
* assertThat(Streams.reverse(Stream.of(1,2,3)).collect(CyclopsCollectors.toList())
,equalTo(Arrays.asList(3,2,1)));
* }
*
*
* @param stream Stream to reverse
* @return Reversed stream
*/
public static Stream reverse(final Stream stream) {
return ReactiveSeq.of(1).flatMap(i->reversedStream(stream.collect(java.util.stream.Collectors.toList())));
}
/**
* Create a reversed Stream from a List
*
* {@code
* Streams.reversedStream(asList(1,2,3))
.map(i->i*100)
.forEach(System.out::println);
assertThat(Streams.reversedStream(Arrays.asList(1,2,3)).collect(CyclopsCollectors.toList())
,equalTo(Arrays.asList(3,2,1)));
*
* }
*
*
* @param list List to create a reversed Stream from
* @return Reversed Stream
*/
public static Stream reversedStream(final List list) {
return new ReversedIterator<>(
list).stream();
}
/**
* Create a new Stream that infiniteable cycles the provided Stream
*
*
* {@code
* assertThat(Streams.cycle(Stream.of(1,2,3))
* .limit(6)
* .collect(CyclopsCollectors.toList()),
* equalTo(Arrays.asList(1,2,3,1,2,3)));
}
*
* @param s Stream to cycle
* @return New cycling stream
*/
public static Stream cycle(final Stream s) {
return cycle(Streamable.fromStream(s));
}
/**
* Create a Stream that infiniteable cycles the provided Streamable
* @param s Streamable to cycle
* @return New cycling stream
*/
public static Stream cycle(final Streamable s) {
return Stream.iterate(s.stream(), s1 -> s.stream())
.flatMap(Function.identity());
}
/**
* Create a Stream that finitely cycles the provided Streamable, provided number of times
*
*
* {@code
* assertThat(Streams.cycle(3,Streamable.of(1,2,2))
.collect(CyclopsCollectors.toList()),
equalTo(Arrays.asList(1,2,2,1,2,2,1,2,2)));
* }
*
* @param s Streamable to cycle
* @return New cycling stream
*/
public static Stream cycle(final long times, final Streamable s) {
return Stream.iterate(s.stream(), s1 -> s.stream())
.limit(times)
.flatMap(Function.identity());
}
/**
* Create a stream from an iterable
*
* {@code
* assertThat(Streams.stream(Arrays.asList(1,2,3))
* .collect(CyclopsCollectors.toList()),
* equalTo(Arrays.asList(1,2,3)));
*
* }
*
* @param it Iterable to convert to a Stream
* @return Stream from iterable
*/
public static Stream stream(final Iterable it) {
return StreamSupport.stream(it.spliterator(), false);
}
public static Stream stream(final Spliterator it) {
return StreamSupport.stream(it, false);
}
/**
* Create a stream from an iterator
*
* {@code
* assertThat(Streams.stream(Arrays.asList(1,2,3).iterator())
* .collect(CyclopsCollectors.toList()),
* equalTo(Arrays.asList(1,2,3)));
* }
*
* @param it Iterator to convert to a Stream
* @return Stream from iterator
*/
public static Stream stream(final Iterator