All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aol.cyclops.streams.StreamUtils Maven / Gradle / Ivy

There is a newer version: 7.3.1
Show newest version
package com.aol.cyclops.streams;


import java.io.BufferedReader;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.BaseStream;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import lombok.AllArgsConstructor;
import lombok.experimental.UtilityClass;

import org.jooq.lambda.Seq;
import org.jooq.lambda.tuple.Tuple2;
import org.jooq.lambda.tuple.Tuple3;
import org.jooq.lambda.tuple.Tuple4;
import org.pcollections.ConsPStack;
import org.pcollections.PStack;

import com.aol.cyclops.closures.mutable.Mutable;
import com.aol.cyclops.internal.AsGenericMonad;
import com.aol.cyclops.invokedynamic.ExceptionSoftener;
import com.aol.cyclops.monad.AnyM;
import com.aol.cyclops.sequence.HeadAndTail;
import com.aol.cyclops.sequence.HotStream;
import com.aol.cyclops.sequence.Monoid;
import com.aol.cyclops.sequence.ReversedIterator;
import com.aol.cyclops.sequence.SeqUtils;
import com.aol.cyclops.sequence.SequenceM;
import com.aol.cyclops.sequence.SequenceMImpl;
import com.aol.cyclops.sequence.future.FutureOperations;
import com.aol.cyclops.sequence.spliterators.ReversableSpliterator;
import com.aol.cyclops.sequence.streamable.AsStreamable;
import com.aol.cyclops.sequence.streamable.Streamable;
import com.aol.cyclops.streams.future.FutureOperationsImpl;
import com.aol.cyclops.streams.operators.BatchBySizeOperator;
import com.aol.cyclops.streams.operators.BatchByTimeAndSizeOperator;
import com.aol.cyclops.streams.operators.BatchByTimeOperator;
import com.aol.cyclops.streams.operators.BatchWhileOperator;
import com.aol.cyclops.streams.operators.DebounceOperator;
import com.aol.cyclops.streams.operators.LimitLastOperator;
import com.aol.cyclops.streams.operators.LimitWhileOperator;
import com.aol.cyclops.streams.operators.LimitWhileTimeOperator;
import com.aol.cyclops.streams.operators.MultiCollectOperator;
import com.aol.cyclops.streams.operators.MultiReduceOperator;
import com.aol.cyclops.streams.operators.OnePerOperator;
import com.aol.cyclops.streams.operators.RecoverOperator;
import com.aol.cyclops.streams.operators.SkipLastOperator;
import com.aol.cyclops.streams.operators.SkipWhileOperator;
import com.aol.cyclops.streams.operators.SkipWhileTimeOperator;
import com.aol.cyclops.streams.operators.WindowByTimeAndSizeOperator;
import com.aol.cyclops.streams.operators.WindowStatefullyWhileOperator;
import com.aol.cyclops.streams.operators.WindowWhileOperator;

@UtilityClass 
public class StreamUtils{
	
	public final static  Optional> streamToOptional(Stream stream){
		List collected = stream.collect(Collectors.toList());
		if(collected.size()==0)
			return Optional.empty();
		return Optional.of(collected);
	}
	public final static  Stream optionalToStream(Optional optional){
		if(optional.isPresent())
			return Stream.of(optional.get());
		return Stream.of();
	}
	public final static  CompletableFuture> streamToCompletableFuture(Stream stream){
		return CompletableFuture.completedFuture(stream.collect(Collectors.toList()));
			
	}
	public final static  Stream completableFutureToStream(CompletableFuture future){
		return Stream.of(future.join());
			
	}
	/**
	 * Split at supplied location 
	 * 
	 * {@code 
	 * SequenceM.of(1,2,3).splitAt(1)
	 * 
	 *  //SequenceM[1], SequenceM[2,3]
	 * }
	 * 
	 * 
*/ public final static Tuple2,Stream> splitAt(Stream stream,int where){ Tuple2,Stream> Tuple2 = duplicate(stream); return new Tuple2(Tuple2.v1.limit(where),Tuple2.v2.skip(where)); } /** * Split stream at point where predicate no longer holds *
	 * {@code
	 *   SequenceM.of(1, 2, 3, 4, 5, 6).splitBy(i->i<4)
	 *   
	 *   //SequenceM[1,2,3] SequenceM[4,5,6]
	 * }
	 * 
*/ public final static Tuple2,Stream> splitBy(Stream stream,Predicate splitter){ Tuple2,Stream> Tuple2 = duplicate(stream); return new Tuple2(limitWhile(Tuple2.v1,splitter),skipWhile(Tuple2.v2,splitter)); } /** * Partition a Stream into two one a per element basis, based on predicate's boolean value *
	 * {@code 
	 *  SequenceM.of(1, 2, 3, 4, 5, 6).partition(i -> i % 2 != 0) 
	 *  
	 *  //SequenceM[1,3,5], SequenceM[2,4,6]
	 * }
	 *
	 * 
*/ public final static Tuple2,Stream> partition(Stream stream,Predicate splitter){ Tuple2,Stream> Tuple2 = duplicate(stream); return new Tuple2(Tuple2.v1.filter(splitter),Tuple2.v2.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, SequenceM> copies =of(1,2,3,4,5,6).duplicate();
		 assertTrue(copies.v1.anyMatch(i->i==2));
		 assertTrue(copies.v2.anyMatch(i->i==2));
	 * 
	 * }
	 * 
* * @return duplicated stream */ public final static Tuple2,Stream> duplicate(Stream stream){ Tuple2,Iterator> Tuple2 = StreamUtils.toBufferingDuplicator(stream.iterator()); return new Tuple2(StreamUtils.stream(Tuple2.v1()),StreamUtils.stream(Tuple2.v2())); } private final static Tuple2,Stream> duplicatePos(Stream stream,int pos){ Tuple2,Iterator> Tuple2 = StreamUtils.toBufferingDuplicator(stream.iterator(),pos); return new Tuple2(StreamUtils.stream(Tuple2.v1()),StreamUtils.stream(Tuple2.v2())); } /** * 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>,SequenceM>,SequenceM>> Tuple3 = sequence.triplicate();
	
	 * }
	 * 
*/ @SuppressWarnings("unchecked") public final static Tuple3,Stream,Stream> triplicate(Stream stream){ Stream> its = StreamUtils.toBufferingCopier(stream.iterator(),3) .stream() .map(it -> StreamUtils.stream(it)); 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>,SequenceM>,SequenceM>,SequenceM>> quad = sequence.quadruplicate();

	 * }
	 * 
* @return */ @SuppressWarnings("unchecked") public final static Tuple4,Stream,Stream,Stream> quadruplicate(Stream stream){ Stream> its = StreamUtils.toBufferingCopier(stream.iterator(),4) .stream() .map(it -> StreamUtils.stream(it)); Iterator> it = its.iterator(); return new Tuple4(it.next(),it.next(),it.next(),it.next()); } /** * Append Stream to this SequenceM * *
	 * {@code 
	 * List result = 	of(1,2,3).appendStream(of(100,200,300))
										.map(it ->it+"!!")
										.collect(Collectors.toList());

			assertThat(result,equalTo(Arrays.asList("1!!","2!!","3!!","100!!","200!!","300!!")));
	 * }
	 * 
* * @param stream to append * @return SequenceM with Stream appended */ public static final Stream appendStream(Stream stream1,Stream append) { return Stream.concat(stream1, append); } /** * Prepend Stream to this SequenceM * *
	 * {@code 
	 * List result = of(1,2,3).prependStream(of(100,200,300))
				.map(it ->it+"!!").collect(Collectors.toList());

			assertThat(result,equalTo(Arrays.asList("100!!","200!!","300!!","1!!","2!!","3!!")));
	 * 
	 * }
	 * 
* * @param stream to Prepend * @return SequenceM with Stream prepended */ public static final Stream prependStream(Stream stream1,Stream prepend) { return Stream.concat(prepend,stream1); } /** * Append values to the end of this SequenceM *
	 * {@code 
	 * List result = 	of(1,2,3).append(100,200,300)
										.map(it ->it+"!!")
										.collect(Collectors.toList());

			assertThat(result,equalTo(Arrays.asList("1!!","2!!","3!!","100!!","200!!","300!!")));
	 * }
	 * 
* @param values to append * @return SequenceM with appended values */ public static final Stream append(Stream stream,T... values) { return appendStream(stream,Stream.of(values)); } /** * Prepend given values to the start of the Stream *
	 * {@code 
	 * List result = 	of(1,2,3).prepend(100,200,300)
				.map(it ->it+"!!").collect(Collectors.toList());

			assertThat(result,equalTo(Arrays.asList("100!!","200!!","300!!","1!!","2!!","3!!")));
	 * }
	 * @param values to prepend
	 * @return SequenceM with values prepended
	 */
	public static final  Stream prepend(Stream stream,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(Collectors.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(Stream stream,int pos, T... values) { Tuple2,Stream> Tuple2 = duplicatePos(stream,pos); return appendStream(append(Tuple2.v1.limit(pos),values),Tuple2.v2.skip(pos)); } /** * Delete elements between given indexes in a Stream *
	 * {@code 
	 * List result = 	of(1,2,3,4,5,6).deleteBetween(2,4)
				.map(it ->it+"!!").collect(Collectors.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(Stream stream,int start,int end) { Tuple2,Stream> Tuple2 = duplicatePos(stream,start); return appendStream(Tuple2.v1.limit(start),Tuple2.v2.skip(end)); } /** * Insert a Stream into the middle of this stream at the specified position *
	 * {@code 
	 * List result = 	of(1,2,3).insertStreamAt(1,of(100,200,300))
				.map(it ->it+"!!").collect(Collectors.toList());

			assertThat(result,equalTo(Arrays.asList("1!!","100!!","200!!","300!!","2!!","3!!")));
	 * }
	 * 
* @param pos to insert Stream at * @param stream to insert * @return newly conjoined SequenceM */ public static final Stream insertStreamAt(Stream stream1,int pos, Stream insert) { Tuple2,Stream> Tuple2 = duplicatePos(stream1,pos); return appendStream(appendStream(Tuple2.v1.limit(pos),insert),Tuple2.v2.skip(pos)); } /** * Convert to a Stream with the result of a reduction operation repeated * specified times * *
	 * {@code 
	 *   		List list = StreamUtils.cycle(Stream.of(1,2,2),Reducers.toCountInt(),3)
	 * 										.
	 * 										.collect(Collectors.toList());
	 * 	//is asList(3,3,3);
	 *   }
	 * 
* * @param m * Monoid to be used in reduction * @param times * Number of times value should be repeated * @return Stream with reduced values repeated */ public final static Stream cycle(Stream stream,Monoid m, int times) { return StreamUtils.cycle(times,AsStreamable.fromObject(m.reduce(stream))); } /** * extract head and tail together * *
	 * {@code 
	 *  Stream helloWorld = Stream.of("hello","world","last");
		HeadAndTail headAndTail = StreamUtils.headAndTail(helloWorld);
		 String head = headAndTail.head();
		 assertThat(head,equalTo("hello"));
		
		SequenceM tail =  headAndTail.tail();
		assertThat(tail.headAndTail().head(),equalTo("world"));
	 * }
	 * 
* * @return */ public final static HeadAndTail headAndTail(Stream stream){ Iterator it = stream.iterator(); return new HeadAndTail(it.next(),sequenceM(stream(it),Optional.empty())); } /** *
	 * {@code 
	 *  Stream helloWorld = Stream.of();
		Optional> headAndTail = StreamUtils.headAndTailOptional(helloWorld);
		assertTrue(!headAndTail.isPresent());
	 * }
	 * 
* @param stream to extract head and tail from * @return */ public final static Optional> headAndTailOptional(Stream stream){ Iterator it = stream.iterator(); if(!it.hasNext()) return Optional.empty(); return Optional.of(new HeadAndTail(it.next(),sequenceM(stream(it),Optional.empty()))); } /** * skip elements in Stream until Predicate holds true *
	 * {@code  StreamUtils.skipUntil(Stream.of(4,3,6,7),i->i==6).collect(Collectors.toList())
	 *  // [6,7]
	 *  }
* @param stream Stream to skip elements from * @param predicate to apply * @return Stream with elements skipped */ public static Stream skipUntil(Stream stream,Predicate predicate){ return skipWhile(stream,predicate.negate()); } public static Stream skipLast(Stream stream,int num){ return new SkipLastOperator<>(stream,num).skipLast(); } public static Stream limitLast(Stream stream,int num){ return new LimitLastOperator<>(stream,num).limitLast(); } public static Stream recover(Stream stream,Function fn){ return new RecoverOperator<>(stream,Throwable.class).recover(fn); } public static Stream recover(Stream stream,Class type, Function fn){ return new RecoverOperator(stream,(Class)type) .recover((Function)fn); } /** * skip elements in a Stream while Predicate holds true * *
	 * 
	 * {@code  StreamUtils.skipWhile(Stream.of(4,3,6,7).sorted(),i->i<6).collect(Collectors.toList())
	 *  // [6,7]
	 *  }
* @param stream * @param predicate * @return */ public static Stream skipWhile(Stream stream,Predicate predicate){ return new SkipWhileOperator(stream).skipWhile(predicate); } public static Stream limit(Stream stream,long time, TimeUnit unit){ return new LimitWhileTimeOperator(stream).limitWhile(time,unit); } public static Stream skip(Stream stream,long time, TimeUnit unit){ return new SkipWhileTimeOperator(stream).skipWhile(time,unit); } /** * Take elements from a stream while the predicates hold *
	 * {@code StreamUtils.limitWhile(Stream.of(4,3,6,7).sorted(),i->i<6).collect(Collectors.toList());
	 * //[4,3]
	 * }
	 * 
* @param stream * @param predicate * @return */ public static Stream limitWhile(Stream stream,Predicate predicate){ return new LimitWhileOperator(stream).limitWhile(predicate); } /** * Take elements from a Stream until the predicate holds *
	 * {@code StreamUtils.limitUntil(Stream.of(4,3,6,7),i->i==6).collect(Collectors.toList());
	 * //[4,3]
	 * }
	 * 
* @param stream * @param predicate * @return */ public static Stream limitUntil(Stream stream,Predicate predicate){ return limitWhile(stream,predicate.negate()); } /** * Reverse a Stream * *
	 * {@code 
	 * assertThat(StreamUtils.reverse(Stream.of(1,2,3)).collect(Collectors.toList())
				,equalTo(Arrays.asList(3,2,1)));
	 * }
	 * 
* * @param stream Stream to reverse * @return Reversed stream */ public static Stream reverse(Stream stream){ return reversedStream(stream.collect(Collectors.toList())); } /** * Create a reversed Stream from a List *
	 * {@code 
	 * StreamUtils.reversedStream(asList(1,2,3))
				.map(i->i*100)
				.forEach(System.out::println);
		
		
		assertThat(StreamUtils.reversedStream(Arrays.asList(1,2,3)).collect(Collectors.toList())
				,equalTo(Arrays.asList(3,2,1)));
	 * 
	 * }
	 * 
* * @param list List to create a reversed Stream from * @return Reversed Stream */ public static Stream reversedStream(List list){ return new ReversedIterator<>(list).stream(); } /** * Create a new Stream that infiniteable cycles the provided Stream * *
	 * {@code 		
	 * assertThat(StreamUtils.cycle(Stream.of(1,2,3))
	 * 						.limit(6)
	 * 						.collect(Collectors.toList()),
	 * 								equalTo(Arrays.asList(1,2,3,1,2,3)));
		}
	 * 
* @param s Stream to cycle * @return New cycling stream */ public static Stream cycle(Stream s){ return cycle(AsStreamable.fromStream(s)); } /** * Create a Stream that infiniteable cycles the provided Streamable * @param s Streamable to cycle * @return New cycling stream */ public static Stream cycle(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(StreamUtils.cycle(3,Streamable.of(1,2,2))
								.collect(Collectors.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(int times,Streamable s){ return Stream.iterate(s.stream(),s1-> s.stream()).limit(times).flatMap(Function.identity()); } /** * Create a stream from an iterable *
	 * {@code 
	 * 	assertThat(StreamUtils.stream(Arrays.asList(1,2,3))
	 * 								.collect(Collectors.toList()),
	 * 									equalTo(Arrays.asList(1,2,3)));

	 * 
	 * }
	 * 
* @param it Iterable to convert to a Stream * @return Stream from iterable */ public static Stream stream(Iterable it){ return StreamSupport.stream(it.spliterator(), false); } public static Stream stream(Spliterator it){ return StreamSupport.stream(it, false); } /** * Create a stream from an iterator *
	 * {@code 
	 * 	assertThat(StreamUtils.stream(Arrays.asList(1,2,3).iterator())	
	 * 							.collect(Collectors.toList()),
	 * 								equalTo(Arrays.asList(1,2,3)));

	 * }
	 * 
* @param it Iterator to convert to a Stream * @return Stream from iterator */ public static Stream stream(Iterator it){ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.ORDERED), false); } /** * Concat an Object and a Stream * If the Object is a Stream, Streamable or Iterable will be converted (or left) in Stream form and concatonated * Otherwise a new Stream.of(o) is created * * @param o Object to concat * @param stream Stream to concat * @return Concatonated Stream */ public static Stream concat(Object o, Stream stream){ Stream first = null; if(o instanceof Stream){ first = (Stream)o; }else if(o instanceof Iterable){ first = stream( (Iterable)o); } else if(o instanceof Streamable){ first = ((Streamable)o).stream(); } else{ first = Stream.of((U)o); } return Stream.concat(first, stream); } /** * Create a stream from a map *
	 * {@code 
	 * 	Map map = new HashMap<>();
		map.put("hello","world");
		assertThat(StreamUtils.stream(map).collect(Collectors.toList()),equalTo(Arrays.asList(new AbstractMap.SimpleEntry("hello","world"))));

	 * }
* * * @param it Iterator to convert to a Stream * @return Stream from a map */ public final static Stream> stream(Map it){ return it.entrySet().stream(); } public final static FutureOperations futureOperations(Stream stream,Executor exec){ return new FutureOperationsImpl(exec,sequenceM(stream,Optional.empty())); } public final static T firstValue(Stream stream){ return stream.findAny().get(); } /** * Simultaneously reduce a stream with multiple reducers * *
{@code
	 * 
	 *  Monoid sum = Monoid.of(0,(a,b)->a+b);
		Monoid mult = Monoid.of(1,(a,b)->a*b);
		val result = StreamUtils.reduce(Stream.of(1,2,3,4),Arrays.asList(sum,mult));
				
		 
		assertThat(result,equalTo(Arrays.asList(10,24)));
		}
* * @param stream Stream to reduce * @param reducers Reducers to reduce Stream * @return Reduced Stream values as List entries */ @SuppressWarnings({"rawtypes","unchecked"}) public static List reduce(Stream stream,Iterable> reducers){ return new MultiReduceOperator(stream).reduce(reducers); } /** * Simultanously reduce a stream with multiple reducers * *
	 * {@code 
	 *  Monoid concat = Monoid.of("",(a,b)->a+b);
		Monoid join = Monoid.of("",(a,b)->a+","+b);
		assertThat(StreamUtils.reduce(Stream.of("hello", "world", "woo!"),Stream.of(concat,join))
		                 ,equalTo(Arrays.asList("helloworldwoo!",",hello,world,woo!")));
	 * }
	 * 
* * @param stream Stream to reduce * @param reducers Reducers to reduce Stream * @return Reduced Stream values as List entries */ @SuppressWarnings({"rawtypes","unchecked"}) public static List reduce(Stream stream,Stream> reducers){ return (List)reduce(stream, (List)reducers.collect(Collectors.toList())); } /** * Apply multiple Collectors, simultaneously to a Stream *
	 * {@code 
	 * List result = StreamUtils.collect(Stream.of(1,2,3),
								Stream.of(Collectors.toList(),
								Collectors.summingInt(Integer::intValue),
								Collectors.averagingInt(Integer::intValue)));
		
		assertThat(result.get(0),equalTo(Arrays.asList(1,2,3)));
		assertThat(result.get(1),equalTo(6));
		assertThat(result.get(2),equalTo(2.0));
	 * }
	 * 
* @param stream Stream to collect * @param collectors Collectors to apply * @return Result as a list */ public static List collect(Stream stream, Stream collectors){ return collect(stream, AsStreamable.fromStream(collectors)); } /** * Apply multiple Collectors, simultaneously to a Stream *
	 * {@code 
	 * List result = StreamUtils.collect(Stream.of(1,2,3),
								Arrays.asList(Collectors.toList(),
								Collectors.summingInt(Integer::intValue),
								Collectors.averagingInt(Integer::intValue)));
		
		assertThat(result.get(0),equalTo(Arrays.asList(1,2,3)));
		assertThat(result.get(1),equalTo(6));
		assertThat(result.get(2),equalTo(2.0));
	 * }
	 * 
* @param stream Stream to collect * @param collectors Collectors to apply * @return Result as a list */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static List collect(Stream stream, Iterable collectors){ return collect(stream, AsStreamable.fromIterable(collectors)); } /** * Apply multiple Collectors, simultaneously to a Stream *
	 * {@code
	 * List result = StreamUtils.collect(Stream.of(1,2,3),
								Streamable.of(Collectors.toList(),
								Collectors.summingInt(Integer::intValue),
								Collectors.averagingInt(Integer::intValue)));
		
		assertThat(result.get(0),equalTo(Arrays.asList(1,2,3)));
		assertThat(result.get(1),equalTo(6));
		assertThat(result.get(2),equalTo(2.0));
	 * 
	 * }
	 * 
* @param stream Stream to collect * @param collectors Collectors to apply * @return Result as a list */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static List collect(Stream stream, Streamable collectors){ return new MultiCollectOperator(stream).collect(collectors); } /** * Repeat in a Stream while specified predicate holds *
	 * {@code 
	 *  int count =0;
	 *  
		assertThat(StreamUtils.cycleWhile(Stream.of(1,2,2)
											,next -> count++<6 )
											.collect(Collectors.toList()),equalTo(Arrays.asList(1,2,2,1,2,2)));
	 * }
	 * 
* @param predicate * repeat while true * @return Repeating Stream */ public final static Stream cycleWhile(Stream stream,Predicate predicate) { return StreamUtils.limitWhile(StreamUtils.cycle(stream),predicate); } /** * Repeat in a Stream until specified predicate holds * *
	 * {@code 
	 * 	count =0;
		assertThat(StreamUtils.cycleUntil(Stream.of(1,2,2,3)
											,next -> count++>10 )
											.collect(Collectors.toList()),equalTo(Arrays.asList(1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2)));

	 * }
	 * 
* @param predicate * repeat while true * @return Repeating Stream */ public final static Stream cycleUntil(Stream stream,Predicate predicate) { return StreamUtils.limitUntil(StreamUtils.cycle(stream),predicate); } /** * Generic zip function. E.g. Zipping a Stream and a Sequence * *
	 * {@code 
	 * Stream> zipped = StreamUtils.zip(Stream.of(1,2,3)
												,SequenceM.of(2,3,4), 
													(a,b) -> Arrays.asList(a,b));
		
		
		List zip = zipped.collect(Collectors.toList()).get(1);
		assertThat(zip.get(0),equalTo(2));
		assertThat(zip.get(1),equalTo(3));
	 * }
	 * 
* @param second * Monad to zip with * @param zipper * Zipping function * @return Stream zipping two Monads */ public final static Stream zipSequence(Stream stream,Stream second, BiFunction zipper) { Iterator left = stream.iterator(); Iterator right = second.iterator(); return StreamUtils.stream(new Iterator(){ @Override public boolean hasNext() { return left.hasNext() && right.hasNext(); } @Override public R next() { return zipper.apply(left.next(), right.next()); } }); } /** * Generic zip function. E.g. Zipping a Stream and an Optional * *
	 * {@code
	 * Stream> zipped = StreamUtils.zip(Stream.of(1,2,3)
										,anyM(Optional.of(2)), 
											(a,b) -> Arrays.asList(a,b));
		
		
		List zip = zipped.collect(Collectors.toList()).get(0);
		assertThat(zip.get(0),equalTo(1));
		assertThat(zip.get(1),equalTo(2));
	 * 
	 * }
	 * 
*/ public final static Stream zipAnyM(Stream stream,AnyM second, BiFunction zipper) { return zipSequence(stream,second.toSequence(), zipper); } /** * Zip this Monad with a Stream *
	   {@code 
	   Stream> zipped = StreamUtils.zipStream(Stream.of(1,2,3)
												,Stream.of(2,3,4), 
													(a,b) -> Arrays.asList(a,b));
		
		
		List zip = zipped.collect(Collectors.toList()).get(1);
		assertThat(zip.get(0),equalTo(2));
		assertThat(zip.get(1),equalTo(3));
	   }
	   
* * @param second * Stream to zip with * @param zipper * Zip funciton * @return This monad zipped with a Stream */ public final static Stream zipStream(Stream stream,BaseStream> second, BiFunction zipper) { Iterator left = stream.iterator(); Iterator right = second.iterator(); return StreamUtils.stream(new Iterator(){ @Override public boolean hasNext() { return left.hasNext() && right.hasNext(); } @Override public R next() { return zipper.apply(left.next(), right.next()); } }); } /** * Create a sliding view over this Stream *
	 * {@code 
	 * List> list = StreamUtils.sliding(Stream.of(1,2,3,4,5,6)
												,2,1)
									.collect(Collectors.toList());
		
	
		assertThat(list.get(0),hasItems(1,2));
		assertThat(list.get(1),hasItems(2,3));
	 * }
	 * 
* @param windowSize * Size of sliding window * @return Stream with sliding view over monad */ public final static Stream> sliding(Stream stream,int windowSize,int increment) { Iterator it = stream.iterator(); Mutable> list = Mutable.of(ConsPStack.empty()); return StreamUtils.stream(new Iterator>(){ @Override public boolean hasNext() { return it.hasNext(); } @Override public List next() { for(int i=0;i0;i++) list.mutate(var -> var.minus(0)); for (int i = 0; list.get().size() < windowSize && it.hasNext(); i++) { if(it.hasNext()){ list.mutate(var -> var.plus(Math.max(0,var.size()),it.next())); } } return list.get(); } }); } /** * Create a sliding view over this Stream *
	 * {@code 
	 * List> list = StreamUtils.sliding(Stream.of(1,2,3,4,5,6)
												,2,1)
									.collect(Collectors.toList());
		
	
		assertThat(list.get(0),hasItems(1,2));
		assertThat(list.get(1),hasItems(2,3));
	 * }
	 * 
* @param windowSize * Size of sliding window * @return Stream with sliding view over monad */ public final static Stream> window(Stream stream,int windowSize,int increment) { Iterator it = stream.iterator(); Mutable> list = Mutable.of(ConsPStack.empty()); return StreamUtils.stream(new Iterator>(){ @Override public boolean hasNext() { return it.hasNext(); } @Override public Streamable next() { for(int i=0;i0;i++) list.mutate(var -> var.minus(0)); for (int i = 0; list.get().size() < windowSize && it.hasNext(); i++) { if(it.hasNext()){ list.mutate(var -> var.plus(Math.max(0,var.size()),it.next())); } } return Streamable.fromIterable(list.get()); } }); } /** * Create a sliding view over this Stream *
	 * {@code 
	 * List> list = StreamUtils.sliding(Stream.of(1,2,3,4,5,6)
												,2)
									.collect(Collectors.toList());
		
	
		assertThat(list.get(0),hasItems(1,2));
		assertThat(list.get(1),hasItems(2,3));
	 * }
	 * 
* * @param stream Stream to create sliding view on * @param windowSize size of window * @return */ public final static Stream> sliding(Stream stream,int windowSize) { return sliding(stream,windowSize,1); } /** * Group elements in a Monad into a Stream *
	   {@code 
	 * 	List> list = StreamUtils.grouped(Stream.of(1,2,3,4,5,6)
														,3)
													.collect(Collectors.toList());
		
		
		assertThat(list.get(0),hasItems(1,2,3));
		assertThat(list.get(1),hasItems(4,5,6));
		}
	 * 
* @param groupSize * Size of each Group * @return Stream with elements grouped by size */ public final static Stream> batchBySize(Stream stream,int groupSize) { return new BatchBySizeOperator>(stream).batchBySize(groupSize); } public final static> Stream batchBySize(Stream stream,int groupSize, Supplier factory) { return new BatchBySizeOperator(stream,factory).batchBySize(groupSize); } public final static Streamable shuffle(Stream stream){ List list = stream.collect(Collectors.toList()); Collections.shuffle(list); return Streamable.fromIterable(list); } public final static Streamable toLazyStreamable(Stream stream){ return AsStreamable.fromStream(stream); } public final static Streamable toConcurrentLazyStreamable(Stream stream){ return AsStreamable.synchronizedFromStream(stream); } public final static Stream scanRight(Stream stream,U identity,BiFunction combiner){ return Seq.seq(stream).scanRight(identity,combiner); } /** * Scan left using supplied Monoid * *
	 * {@code  
	 * 
	 * 	assertEquals(asList("", "a", "ab", "abc"),
	 * 					StreamUtils.scanLeft(Stream.of("a", "b", "c"),Reducers.toString(""))
	 * 			.collect(Collectors.toList());
	 *         
	 *         }
	 * 
* * @param monoid * @return */ public final static Stream scanLeft(Stream stream,Monoid monoid) { Iterator it = stream.iterator(); return StreamUtils.stream(new Iterator() { boolean init = false; T next = monoid.zero(); @Override public boolean hasNext() { if(!init) return true; return it.hasNext(); } @Override public T next() { if (!init) { init = true; return monoid.zero(); } return next = monoid.combiner().apply(next, it.next()); } }); } /** * Check that there are specified number of matches of predicate in the Stream * *
	 * {@code 
	 *  assertTrue(StreamUtils.xMatch(Stream.of(1,2,3,5,6,7),3, i->i>4));
	 * }
	 * 
* */ public static boolean xMatch(Stream stream,int num, Predicate c) { return stream.filter(t -> c.test(t)) .collect(Collectors.counting()) == num; } /** *
	 * {@code 
     * assertThat(StreamUtils.noneMatch(of(1,2,3,4,5),it-> it==5000),equalTo(true));
     * }
     * 
* */ public final static boolean noneMatch(Stream stream,Predicate c) { return stream.allMatch(c.negate()); } public final static String join(Stream stream){ return stream.map(t->t.toString()).collect(Collectors.joining()); } public final static String join(Stream stream,String sep){ return stream.map(t->t.toString()).collect(Collectors.joining(sep)); } public final static String join(Stream stream, String sep,String start,String end){ return stream.map(t->t.toString()).collect(Collectors.joining(sep,start,end)); } public final static > Optional minBy(Stream stream,Function f){ Optional> o = stream.map(in->new Tuple2(f.apply(in),in)).min(Comparator.comparing(n->n.v1(),Comparator.naturalOrder())); return o.map(p->p.v2()); } public final static Optional min(Stream stream,Comparator comparator){ return stream.collect(Collectors.minBy(comparator)); } public final static > Optional maxBy(Stream stream,Function f){ Optional> o = stream.map(in->new Tuple2(f.apply(in),in)).max(Comparator.comparing(n->n.v1(),Comparator.naturalOrder())); return o.map(p->p.v2()); } public final static Optional max(Stream stream,Comparator comparator){ return stream.collect(Collectors.maxBy(comparator)); } /** * Attempt to map this Monad to the same type as the supplied Monoid (using mapToType on the monoid interface) * Then use Monoid to reduce values * * @param reducer Monoid to reduce values * @return Reduce result */ public final static R mapReduce(Stream stream,Monoid reducer){ return reducer.mapReduce(stream); } /** * Attempt to map this Monad to the same type as the supplied Monoid, using supplied function * Then use Monoid to reduce values * * @param mapper Function to map Monad type * @param reducer Monoid to reduce values * @return Reduce result */ public final static R mapReduce(Stream stream,Function mapper, Monoid reducer){ return reducer.reduce(stream.map(mapper)); } /** * * * @param reducer Use supplied Monoid to reduce values starting via foldLeft * @return Reduced result */ public final static T foldLeft(Stream stream,Monoid reducer){ return reducer.reduce(stream); } /** * Attempt to map this Monad to the same type as the supplied Monoid (using mapToType on the monoid interface) * Then use Monoid to reduce values * * @param reducer Monoid to reduce values * @return Reduce result */ public final static T foldLeftMapToType(Stream stream,Monoid reducer){ return reducer.mapReduce(stream); } /** * * * @param reducer Use supplied Monoid to reduce values starting via foldRight * @return Reduced result */ public final static T foldRight(Stream stream,Monoid reducer){ return reducer.reduce(StreamUtils.reverse(stream)); } /** * Attempt to map this Monad to the same type as the supplied Monoid (using mapToType on the monoid interface) * Then use Monoid to reduce values * * @param reducer Monoid to reduce values * @return Reduce result */ public final static T foldRightMapToType(Stream stream,Monoid reducer){ return reducer.mapReduce(StreamUtils.reverse(stream)); } /** * @return Underlying monad converted to a Streamable instance */ public final static Streamable toStreamable(Stream stream){ return AsStreamable.fromStream(stream); } /** * @return This monad converted to a set */ public final static Set toSet(Stream stream){ return (Set)stream.collect(Collectors.toSet()); } /** * @return this monad converted to a list */ public final static List toList(Stream stream){ return (List)stream.collect(Collectors.toList()); } /** * *
{@code 
	 * assertTrue(StreamUtils.startsWith(Stream.of(1,2,3,4),Arrays.asList(1,2,3)));
	 * }
* * @param iterable * @return True if Monad starts with Iterable sequence of data */ public final static boolean startsWith(Stream stream,Iterable iterable){ return startsWith(stream,iterable.iterator()); } public final static boolean endsWith(Stream stream,Iterable iterable){ Iterator it = iterable.iterator(); List compare1 = new ArrayList<>(); while(it.hasNext()){ compare1.add(it.next()); } LinkedList list = new LinkedList<>(); stream.forEach(v -> { list.add(v); if(list.size()>compare1.size()) list.remove(); }); return startsWith(list.stream(),compare1.iterator()); } /** *
	 * {@code
	 * 		 assertTrue(StreamUtils.startsWith(Stream.of(1,2,3,4),Arrays.asList(1,2,3).iterator())) 
	 * }
* @param iterator * @return True if Monad starts with Iterators sequence of data */ public final static boolean startsWith(Stream stream,Iterator iterator){ Iterator it = stream.iterator(); while(iterator.hasNext()){ if(!it.hasNext()) return false; if(!Objects.equals(it.next(), iterator.next())) return false; } return true; } public final static SequenceM sequenceM(Stream stream,Optional rev){ if(stream instanceof SequenceM) return (SequenceM)stream; if(rev.isPresent()) return new SequenceMImpl(stream,rev.get()); return new SequenceMImpl(stream); } /** * Returns a stream with a given value interspersed between any two values * of this stream. * *
	 * {@code 
	 * assertThat(Arrays.asList(1, 0, 2, 0, 3, 0, 4),
	 * 			equalTo( StreamUtils.intersperse(Stream.of(1, 2, 3, 4),0));
	 * }
	 * 
*/ public static Stream intersperse(Stream stream, T value) { return stream.flatMap(t -> Stream.of(value, t)).skip(1); } /** * Keep only those elements in a stream that are of a given type. * * * assertThat(Arrays.asList(1, 2, 3), * equalTo( StreamUtils.ofType(Stream.of(1, "a", 2, "b", 3,Integer.class)); * */ @SuppressWarnings("unchecked") public static Stream ofType(Stream stream, Class type) { return stream.filter(type::isInstance).map(t -> (U) t); } /** * Cast all elements in a stream to a given type, possibly throwing a * {@link ClassCastException}. * *
	 * {@code
	 * StreamUtils.cast(Stream.of(1, "a", 2, "b", 3),Integer.class)
	 *  // throws ClassCastException
	 *  }
	 */
	public static  Stream cast(Stream stream, Class type) {
		return stream.map(type::cast);
	}
	/**
	 * flatMap operation
	 * 
	 * {@code 
	 * 		assertThat(StreamUtils.flatMapSequenceM(Stream.of(1,2,3),
	 * 							i->SequenceM.of(i+2)).collect(Collectors.toList()),
	 * 								equalTo(Arrays.asList(3,4,5)));
	 * }
	 * 
* @param fn * @return */ public final static Stream flatMapSequenceM(Stream stream,Function> fn) { return stream.flatMap(fn); } public final static Stream flatMapAnyM(Stream stream,Function> fn) { return AsGenericMonad.,T>asMonad(stream).bind(in -> fn.apply(in).unwrap()).sequence(); } /** * flatMap operation that allows a Collection to be returned *
	 * {@code 
	 * 	assertThat(StreamUtils.flatMapCollection(Stream.of(20),i->Arrays.asList(1,2,i))
	 * 								.collect(Collectors.toList()),
	 * 								equalTo(Arrays.asList(1,2,20)));

	 * }
	 * 
* */ public final static Stream flatMapCollection(Stream stream,Function> fn) { return stream.flatMap(fn.andThen(c->c.stream())); } /** *
	 * {@code 
	 * 	assertThat(StreamUtils.flatMapStream(Stream.of(1,2,3),
	 * 							i->Stream.of(i)).collect(Collectors.toList()),
	 * 							equalTo(Arrays.asList(1,2,3)));

	 * 
	 * }
	 * 
* */ public final static Stream flatMapStream(Stream stream,Function> fn) { return stream.flatMap(fn.andThen(bs -> { if(bs instanceof Stream) return (Stream)bs; else return StreamUtils.stream(bs.iterator()); })); } /** * cross type flatMap, removes null entries *
     * {@code 
     * 	 assertThat(StreamUtils.flatMapOptional(Stream.of(1,2,3,null),
     * 										Optional::ofNullable)
     * 										.collect(Collectors.toList()),
     * 										equalTo(Arrays.asList(1,2,3)));

     * }
     * 
*/ public final static Stream flatMapOptional(Stream stream,Function> fn) { return stream.flatMap( in->StreamUtils.optionalToStream(fn.apply(in))); } public final static SequenceM flatten(Stream stream) { return AsGenericMonad.asMonad(stream).flatten().sequence(); } /** *
	 * {@code 
	 * 	assertThat(StreamUtils.flatMapCompletableFuture(Stream.of(1,2,3),
	 * 								i->CompletableFuture.completedFuture(i+2))
	 * 								.collect(Collectors.toList()),
	 * 								equalTo(Arrays.asList(3,4,5)));

	 * }
	 *
*/ public final static Stream flatMapCompletableFuture(Stream stream,Function> fn) { return stream.flatMap( in->StreamUtils.completableFutureToStream(fn.apply(in))); } /** * Perform a flatMap operation where the result will be a flattened stream of Characters * from the CharSequence returned by the supplied function. * *
	 * {@code 
	 *   List result = StreamUtils.liftAndBindCharSequence(Stream.of("input.file"),
									.i->"hello world")
									.toList();
		
		assertThat(result,equalTo(Arrays.asList('h','e','l','l','o',' ','w','o','r','l','d')));
	 * }
	 * 
* * @param fn * @return *///rename -flatMapCharSequence public final static Stream flatMapCharSequence(Stream stream,Function fn) { return AsGenericMonad.,T>asMonad(stream).liftAndBind(fn).sequence(); } /** * Perform a flatMap operation where the result will be a flattened stream of Strings * from the text loaded from the supplied files. * *
	 * {@code
	 * 
		List result = StreamUtils.liftAndBindFile(Stream.of("input.file")
								.map(getClass().getClassLoader()::getResource)
								.peek(System.out::println)
								.map(URL::getFile)
								,File::new)
								.toList();
		
		assertThat(result,equalTo(Arrays.asList("hello","world")));
	 * 
	 * }
	 * 
	 * 
* * @param fn * @return */ public final static Stream flatMapFile(Stream stream,Function fn) { return AsGenericMonad.,T>asMonad(stream).liftAndBind(fn).sequence(); } /** * Perform a flatMap operation where the result will be a flattened stream of Strings * from the text loaded from the supplied URLs * *
	 * {@code 
	 * List result = StreamUtils.liftAndBindURL(Stream.of("input.file")
								,getClass().getClassLoader()::getResource)
								.collect(Collectors.toList();
		
		assertThat(result,equalTo(Arrays.asList("hello","world")));
	 * 
	 * }
	 * 
* * @param fn * @return */ public final static Stream flatMapURL(Stream stream,Function fn) { return AsGenericMonad.,T>asMonad(stream).liftAndBind(fn).sequence(); } /** * Perform a flatMap operation where the result will be a flattened stream of Strings * from the text loaded from the supplied BufferedReaders * *
	 * List result = StreamUtils.liftAndBindBufferedReader(Stream.of("input.file")
								.map(getClass().getClassLoader()::getResourceAsStream)
								.map(InputStreamReader::new)
								,BufferedReader::new)
								.collect(Collectors.toList();
		
		assertThat(result,equalTo(Arrays.asList("hello","world")));
	 * 
	 * 
* * * @param fn * @return */ public final static Stream flatMapBufferedReader(Stream stream,Function fn) { return AsGenericMonad.,T>asMonad(stream).liftAndBind(fn).sequence(); } public static final Tuple2,Iterator> toBufferingDuplicator(Iterator iterator) { return toBufferingDuplicator(iterator,Long.MAX_VALUE); } public static final Tuple2,Iterator> toBufferingDuplicator(Iterator iterator,long pos) { LinkedList bufferTo = new LinkedList(); LinkedList bufferFrom = new LinkedList(); return new Tuple2(new DuplicatingIterator(bufferTo,bufferFrom,iterator,Long.MAX_VALUE,0), new DuplicatingIterator(bufferFrom,bufferTo,iterator,pos,0)); } public static final List> toBufferingCopier(Iterator iterator,int copies) { List> result = new ArrayList<>(); List> leaderboard = new LinkedList<>(); LinkedList buffer = new LinkedList<>(); for(int i=0;i implements Iterator{ LinkedList bufferTo; LinkedList bufferFrom; Iterator it; long otherLimit=Long.MAX_VALUE; long counter=0; @Override public boolean hasNext() { if(bufferFrom.size()>0 || it.hasNext()) return true; return false; } @Override public T next() { try{ if(bufferFrom.size()>0) return bufferFrom.remove(0); else{ T next = it.next(); if(counter implements Iterator{ LinkedList buffer; Iterator it; List> leaderboard = new LinkedList<>(); boolean added= false; int total = 0; int counter=0; @Override public boolean hasNext() { if(isLeader()) return it.hasNext(); if(isLast()) return buffer.size()>0 || it.hasNext(); if(it.hasNext()) return true; return counter < buffer.size(); } private boolean isLeader() { return leaderboard.size()==0 || this==leaderboard.get(0); } private boolean isLast() { return leaderboard.size()==total && this==leaderboard.get(leaderboard.size()-1); } @Override public T next() { if(!added){ this.leaderboard.add(this); added = true; } if(isLeader()){ return handleLeader(); } if(isLast()){ if(buffer.size()>0) return buffer.poll(); return it.next(); } if(counter< buffer.size()) return buffer.get(counter++); return handleLeader(); //exceed buffer, now leading } private T handleLeader() { T next = it.next(); buffer.offer(next); return next; } public CopyingIterator(Iterator it, List> leaderboard,LinkedList buffer,int total) { this.it = it; this.leaderboard = leaderboard; this.buffer = buffer; this.total = total; } } /** * Projects an immutable collection of this stream. Initial iteration over the collection is not thread safe * (can't be performed by multiple threads concurrently) subsequent iterations are. * * @return An immutable collection of this stream. */ public static final Collection toLazyCollection(Stream stream) { return SeqUtils.toLazyCollection(stream.iterator()); } public static final Collection toLazyCollection(Iterator iterator){ return SeqUtils.toLazyCollection(iterator); } /** * Lazily constructs a Collection from specified Stream. Collections iterator may be safely used * concurrently by multiple threads. * @param stream * @return */ public static final Collection toConcurrentLazyCollection(Stream stream) { return SeqUtils.toConcurrentLazyCollection(stream.iterator()); } public static final Collection toConcurrentLazyCollection(Iterator iterator){ return SeqUtils.toConcurrentLazyCollection(iterator); } public final static Stream> windowByTime(Stream stream, long time, TimeUnit t){ Iterator it = stream.iterator(); long toRun = t.toNanos(time); return StreamUtils.stream(new Iterator>(){ long start = System.nanoTime(); @Override public boolean hasNext() { return it.hasNext(); } @Override public Streamable next() { List list = new ArrayList<>(); while(System.nanoTime()-start< toRun && it.hasNext()){ list.add(it.next()); } if(list.size()==0 && it.hasNext()) //time unit may be too small list.add(it.next()); start = System.nanoTime(); return Streamable.fromIterable(list); } }); } public final static Stream> batchByTime(Stream stream, long time, TimeUnit t){ return new BatchByTimeOperator>(stream).batchByTime(time,t); } public final static > Stream batchByTime(Stream stream, long time, TimeUnit t, Supplier factory){ return new BatchByTimeOperator(stream,factory).batchByTime(time,t); } private static final Object UNSET = new Object(); public final static Stream> windowStatefullyWhile(Stream stream,BiPredicate,T> predicate){ return new WindowStatefullyWhileOperator<>(stream).windowStatefullyWhile(predicate); } public final static Stream> windowWhile(Stream stream,Predicate predicate){ return new WindowWhileOperator<>(stream).windowWhile(predicate); } public final static Stream> batchWhile(Stream stream,Predicate predicate){ return new BatchWhileOperator>(stream).batchWhile(predicate); } public final static > Stream batchWhile(Stream stream,Predicate predicate,Supplier factory){ return new BatchWhileOperator(stream,factory).batchWhile(predicate); } public final static Stream> batchUntil(Stream stream,Predicate predicate){ return batchWhile(stream,predicate.negate()); } public final static Stream> batchBySizeAndTime(Stream stream,int size, long time, TimeUnit t){ return new BatchByTimeAndSizeOperator>(stream).batchBySizeAndTime(size, time, t); } public final static > Stream batchBySizeAndTime(Stream stream,int size, long time, TimeUnit t,Supplier factory){ return new BatchByTimeAndSizeOperator(stream,factory).batchBySizeAndTime(size, time, t); } public final static Stream> windowBySizeAndTime(Stream stream,int size, long time, TimeUnit t){ return new WindowByTimeAndSizeOperator<>(stream).windowBySizeAndTime(size, time, t); } public final static Stream debounce(Stream stream, long time, TimeUnit t){ return new DebounceOperator<>(stream).debounce(time, t); } public final static Stream onePer(Stream stream, long time, TimeUnit t){ return new OnePerOperator<>(stream).onePer(time,t); } public final static Stream jitter(Stream stream,long jitterInNanos){ Iterator it = stream.iterator(); Random r = new Random(); return StreamUtils.stream(new Iterator(){ @Override public boolean hasNext() { return it.hasNext(); } @Override public T next() { T nextValue = it.next(); try { long elapsedNanos= (long)(jitterInNanos * r.nextDouble()); long millis = elapsedNanos/1000000; int nanos = (int)(elapsedNanos - millis*1000000); Thread.sleep(Math.max(0,millis),Math.max(0,nanos)); } catch (InterruptedException e) { ExceptionSoftener.throwSoftenedException(e); return null; } return nextValue; } }); } public final static Stream fixedDelay(Stream stream,long time, TimeUnit unit){ Iterator it = stream.iterator(); return StreamUtils.stream(new Iterator(){ @Override public boolean hasNext() { return it.hasNext(); } @Override public T next() { T nextValue = it.next(); try { long elapsedNanos= unit.toNanos(time); long millis = elapsedNanos/1000000; int nanos = (int)(elapsedNanos - millis*1000000); Thread.sleep(Math.max(0,millis),Math.max(0,nanos)); } catch (InterruptedException e) { ExceptionSoftener.throwSoftenedException(e); return null; } return nextValue; } }); } public final static Stream xPer(Stream stream,int x, long time, TimeUnit t){ Iterator it = stream.iterator(); long next = t.toNanos(time); return StreamUtils.stream(new Iterator(){ volatile long last = -1; volatile int count=0; @Override public boolean hasNext() { return it.hasNext(); } @Override public T next() { T nextValue = it.next(); if(count++ HotStream hotStream(Stream stream,Executor exec){ return new HotStreamImpl<>(stream).init(exec); } }