functionalj.stream.StreamPlus Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of functionalj-core Show documentation
Show all versions of functionalj-core Show documentation
The module for FunctionalJ Core.
// ============================================================================
// Copyright (c) 2017-2019 Nawapunth Manusitthipol (NawaMan - http://nawaman.net).
// ----------------------------------------------------------------------------
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// ============================================================================
package functionalj.stream;
import static functionalj.function.Func.f;
import static functionalj.function.Func.themAll;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.UnaryOperator;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import functionalj.function.Func1;
import functionalj.function.Func2;
import functionalj.function.FuncUnit1;
import functionalj.functions.StrFuncs;
import functionalj.functions.ThrowFuncs;
import functionalj.list.FuncList;
import functionalj.list.ImmutableList;
import functionalj.pipeable.Pipeable;
import functionalj.promise.DeferAction;
import functionalj.promise.UncompletedAction;
import functionalj.result.NoMoreResultException;
import functionalj.result.Result;
import functionalj.tuple.Tuple2;
import lombok.val;
// TODO - Intersect
@SuppressWarnings("javadoc")
@FunctionalInterface
public interface StreamPlus
extends
Stream,
Iterable,
StreamPlusWithMapFirst,
StreamPlusWithMapThen,
StreamPlusWithMapTuple,
StreamPlusWithMapToMap,
StreamPlusWithSplit,
StreamPlusWithFillNull,
StreamPlusWithSegment,
StreamPlusWithCombine,
StreamPlusWithCalculate,
StreamPlusAddtionalOperators,
StreamPlusAdditionalTerminalOperators
{
/**
* Throw a no more element exception. This is used for generter.
* This is done in the way that it can be overriden.
**/
public static D noMoreElement() throws NoMoreResultException {
ThrowFuncs.doThrowFrom(()->new NoMoreResultException());
return (D)null;
}
/**
* Returns an empty StreamPlus.
*/
public static StreamPlus empty() {
return StreamPlus
.from(Stream.empty());
}
/**
* Returns an empty StreamPlus.
*/
public static StreamPlus emptyStreamPlus() {
return StreamPlus
.from(Stream.empty());
}
/** Create a StreamPlus from the given data. */
@SafeVarargs
public static StreamPlus of(D ... data) {
return ArrayBackedStream
.from(data);
}
/** Create a StreamPlus from the given data */
@SafeVarargs
public static StreamPlus steamOf(D ... data) {
return of(data);
}
/** Create a StreamPlus from the given stream. */
@SuppressWarnings({ "unchecked", "rawtypes" })
public static StreamPlus from(Stream stream) {
if (stream == null)
return StreamPlus.empty();
return (stream instanceof StreamPlus)
? (StreamPlus)stream
: (StreamPlus)(()->stream);
}
/** Create a StreamPlus from the given iterator. */
public static StreamPlus from(Iterator iterator) {
return IteratorPlus.from(iterator).stream();
}
/** Create a StreamPlus from the given enumeration. */
public static StreamPlus from(Enumeration enumeration) {
Iterable iterable = new Iterable() {
public Iterator iterator() {
return new Iterator() {
private D next;
@Override
public boolean hasNext() {
try {
next = enumeration.nextElement();
return true;
} catch (NoSuchElementException e) {
return false;
}
}
@Override
public D next() {
return next;
}
};
}
};
return StreamPlus.from(StreamSupport.stream(iterable.spliterator(), false));
}
/** Create a StreamPlus that is the repeat of the given array of data. */
@SuppressWarnings("unchecked")
public static StreamPlus repeat(D ... data) {
return cycle(data);
}
/** Create a StreamPlus that is the repeat of the given list of data. */
public static StreamPlus repeat(FuncList data) {
return cycle(data);
}
/** Create a StreamPlus that is the repeat of the given array of data. */
@SafeVarargs
public static StreamPlus cycle(D ... data) {
val size = data.length;
return StreamPlus.from(
IntStream
.iterate(0, i -> i + 1)
.mapToObj(i -> data[i % size]));
}
/** Create a StreamPlus that is the repeat of the given list of data. */
public static StreamPlus cycle(FuncList data) {
val size = data.size();
return StreamPlus.from(
IntStream
.iterate(0, i -> i + 1)
.mapToObj(i -> data.get(i % size)));
}
/** Create a StreamPlus that for a loop with the number of time given - the value is the index of the loop. */
public static StreamPlus loop(int time) {
return StreamPlus
.infiniteInt()
.limit(time);
}
/** Create a StreamPlus that for an infinite loop - the value is the index of the loop. */
public static StreamPlus loop() {
return StreamPlus
.infiniteInt();
}
/** Create a StreamPlus that for an infinite loop - the value is the index of the loop. */
public static StreamPlus infiniteInt() {
return IntStreamPlus
.from(
IntStream
.iterate(0, i -> i + 1))
.mapToObj(i -> i);
}
/** Create a StreamPlus that for a loop from the start value inclusively to the end value exclusively. */
public static StreamPlus range(int startInclusive, int endExclusive) {
return IntStreamPlus
.range(startInclusive, endExclusive)
.mapToObj(i -> i);
}
/** Concatenate all the given streams. */
// Because people know this.
@SafeVarargs
public static StreamPlus concat(Stream ... streams) {
return StreamPlus
.of (streams)
.flatMap(themAll());
}
/** Concatenate all streams supplied by the given supplied. */
@SafeVarargs
public static StreamPlus concat(Supplier> ... streams) {
return StreamPlus
.of (streams)
.map (Supplier::get)
.flatMap(themAll());
}
/**
* Create a StreamPlus from the supplier.
* The supplier will be repeatedly asked for value until NoMoreResultException is thrown.
**/
public static StreamPlus generate(Supplier supplier) {
return StreamPlus
.from(Stream.generate(supplier));
}
/**
* Create a StreamPlus from the supplier.
* The supplier will be repeatedly asked for value until NoMoreResultException is thrown.
**/
public static StreamPlus generateWith(Supplier supplier) {
Iterable iterable = new Iterable() {
public Iterator iterator() {
return new Iterator() {
private D next;
@Override
public boolean hasNext() {
try {
next = supplier.get();
return true;
} catch (NoMoreResultException e) {
return false;
}
}
@Override
public D next() {
return next;
}
};
}
};
return StreamPlus
.from(StreamSupport.stream(iterable.spliterator(), false));
}
/**
* Create a StreamPlus by apply the function to the seed over and over.
*
* For example: let say seed = 1 and f(x) = x*2.
* The result stream will be:
* 1 <- seed,
* 2 <- (1*2),
* 4 <- ((1*2)*2),
* 8 <- (((1*2)*2)*2),
* 16 <- ((((1*2)*2)*2)*2)
* ...
*
* Note: this is an alias of compound()
**/
public static StreamPlus iterate(D seed, UnaryOperator f) {
return StreamPlus
.from(Stream.iterate(seed, f));
}
/**
* Create a StreamPlus by apply the function to the seed over and over.
*
* For example: let say seed = 1 and f(x) = x*2.
* The result stream will be:
* 1 <- seed,
* 2 <- (1*2),
* 4 <- ((1*2)*2),
* 8 <- (((1*2)*2)*2),
* 16 <- ((((1*2)*2)*2)*2)
* ...
*
* Note: this is an alias of iterate()
**/
public static StreamPlus compound(D seed, UnaryOperator f) {
return iterate(seed, f);
}
/**
* Create a StreamPlus by apply the function to the seeds over and over.
*
* For example: let say seed1 = 1, seed2 = 1 and f(a,b) = a+b.
* The result stream will be:
* 1 <- seed1,
* 1 <- seed2,
* 2 <- (1+1),
* 3 <- (1+2),
* 5 <- (2+3),
* 8 <- (5+8)
* ...
*
* Note: this is an alias of compound()
**/
public static StreamPlus iterate(D seed1, D seed2, BinaryOperator f) {
AtomicInteger counter = new AtomicInteger(0);
AtomicReference d1 = new AtomicReference(seed1);
AtomicReference d2 = new AtomicReference(seed2);
return StreamPlus.generate(()->{
if (counter.getAndIncrement() == 0)
return seed1;
if (counter.getAndIncrement() == 2) // Because, 1 is the second time of the first check.
return seed2;
D i2 = d2.get();
D i1 = d1.getAndSet(i2);
D i = f.apply(i1, i2);
d2.set(i);
return i;
});
}
/**
* Create a StreamPlus by apply the function to the seeds over and over.
*
* For example: let say seed1 = 1, seed2 = 1 and f(a,b) = a+b.
* The result stream will be:
* 1 <- seed1,
* 1 <- seed2,
* 2 <- (1+1),
* 3 <- (1+2),
* 5 <- (2+3),
* 8 <- (5+8)
* ...
*
* Note: this is an alias of iterate()
**/
public static StreamPlus compound(D seed1, D seed2, BinaryOperator f) {
return iterate(seed1, seed2, f);
}
/**
* Create a StreamPlus by combining elements together into a StreamPlus of tuples.
* Only elements with pair will be combined. If this is not desirable, use stream1.zip(stream2).
*
* For example:
* stream1 = [A, B, C, D, E]
* stream2 = [1, 2, 3, 4]
*
* The result stream = [(A,1), (B,2), (C,3), (D,4)].
**/
public static StreamPlus> zipOf(
StreamPlus stream1,
StreamPlus stream2) {
return stream1
.zipWith(stream2, ZipWithOption.RequireBoth);
}
/**
* Create a StreamPlus by combining elements together using the merger function and collected into the result stream.
* Only elements with pair will be combined. If this is not desirable, use stream1.zip(stream2).
*
* For example:
* stream1 = [A, B, C, D, E]
* stream2 = [1, 2, 3, 4]
* merger = a + "+" + b
*
* The result stream = ["A+1", "B+2", "C+3", "D+4"].
**/
public static StreamPlus zipOf(
StreamPlus stream1,
StreamPlus stream2,
Func2 merger) {
return stream1
.zipWith(stream2, ZipWithOption.RequireBoth, merger);
}
//== Stream ==
public Stream stream();
//== Helper functions ==
public default TARGET terminate(
Func1, TARGET> action) {
val stream = stream();
try {
val result = action.apply(stream);
return result;
} finally {
stream.close();
}
}
public default void terminate(
FuncUnit1> action) {
val stream = stream();
try {
action.accept(stream);
} finally {
stream.close();
}
}
public default StreamPlus sequential(Func1, StreamPlus> action) {
return deriveWith(stream -> {
val isParallel = stream.isParallel();
if (!isParallel) {
return action.apply(StreamPlus.from(stream));
}
val resultStream = action.apply(StreamPlus.from(stream.sequential()));
if (resultStream.isParallel())
return resultStream;
return resultStream.parallel();
});
}
public default StreamPlus deriveWith(
Function, Stream> action) {
return StreamPlus.from(
action.apply(
this.stream()));
}
//== Stream sepecific ==
@Override
public default StreamPlus sequential() {
return deriveWith(stream -> {
return stream
.sequential();
});
}
@Override
public default StreamPlus parallel() {
return deriveWith(stream -> {
return stream
.parallel();
});
}
@Override
public default StreamPlus unordered() {
return deriveWith(stream -> {
return stream
.unordered();
});
}
@Override
public default boolean isParallel() {
return stream()
.isParallel();
}
@Override
public default void close() {
stream()
.close();
}
@Override
public default StreamPlus onClose(Runnable closeHandler) {
return deriveWith(stream -> {
return stream
.onClose(closeHandler);
});
}
//== Functionalities ==
@Override
public default IntStreamPlus mapToInt(
ToIntFunction super DATA> mapper) {
val intStreamPlus = IntStreamPlus.from(stream().mapToInt(mapper));
intStreamPlus.onClose(()->{
close();
});
return intStreamPlus;
}
@Override
public default LongStreamPlus mapToLong(
ToLongFunction super DATA> mapper) {
return LongStreamPlus
.from(
stream()
.mapToLong(mapper));
}
@Override
public default DoubleStreamPlus mapToDouble(
ToDoubleFunction super DATA> mapper) {
return DoubleStreamPlus
.from(
stream()
.mapToDouble(mapper));
}
@Override
public default IntStreamPlus flatMapToInt(
Function super DATA, ? extends IntStream> mapper) {
return IntStreamPlus
.from(
stream()
.flatMapToInt(mapper));
}
@Override
public default LongStreamPlus flatMapToLong(
Function super DATA, ? extends LongStream> mapper) {
return LongStreamPlus
.from(
stream()
.flatMapToLong(mapper));
}
@Override
public default DoubleStreamPlus flatMapToDouble(
Function super DATA, ? extends DoubleStream> mapper) {
return DoubleStreamPlus
.from(
stream()
.flatMapToDouble(mapper));
}
@Override
public default StreamPlus map(
Function super DATA, ? extends TARGET> mapper) {
return deriveWith(stream -> {
return stream
.map(mapper);
});
}
@Override
public default StreamPlus flatMap(
Function super DATA, ? extends Stream extends TARGET>> mapper) {
return deriveWith(stream -> {
return stream
.flatMap(mapper);
});
}
@Override
public default StreamPlus filter(
Predicate super DATA> predicate) {
return deriveWith(stream -> {
return (predicate == null)
? stream
: stream.filter(predicate);
});
}
@Override
public default StreamPlus filter(
Function super DATA, T> mapper,
Predicate super T> theCondition) {
return filter(value -> {
val target = mapper.apply(value);
val isPass = theCondition.test(target);
return isPass;
});
}
@Override
public default StreamPlus peek(
Consumer super DATA> action) {
return deriveWith(stream -> {
return (action == null)
? stream
: stream.peek(action);
});
}
//-- Limit/Skip --
@Override
public default StreamPlus limit(long maxSize) {
return deriveWith(stream -> {
return stream
.limit(maxSize);
});
}
@Override
public default StreamPlus skip(long n) {
return deriveWith(stream -> {
return stream
.skip(n);
});
}
public default StreamPlus limit(Long maxSize) {
return deriveWith(stream -> {
return ((maxSize == null) || (maxSize.longValue() < 0))
? stream
: stream.limit(maxSize);
});
}
public default StreamPlus skip(Long startAt) {
return deriveWith(stream -> {
return ((startAt == null) || (startAt.longValue() < 0))
? stream
: stream.skip(startAt);
});
}
public default StreamPlus skipWhile(Predicate super DATA> condition) {
return sequential(stream -> {
val isStillTrue = new AtomicBoolean(true);
return stream.filter(e -> {
if (!isStillTrue.get())
return true;
if (!condition.test(e))
isStillTrue.set(false);
return !isStillTrue.get();
});
});
}
public default StreamPlus skipUntil(Predicate super DATA> condition) {
return sequential(stream -> {
val isStillTrue = new AtomicBoolean(true);
return stream.filter(e -> {
if (!isStillTrue.get())
return true;
if (condition.test(e))
isStillTrue.set(false);
return !isStillTrue.get();
});
});
}
public default StreamPlus takeWhile(Predicate super DATA> condition) {
// https://stackoverflow.com/questions/32290278/picking-elements-of-a-list-until-condition-is-met-with-java-8-lambdas
return sequential(stream -> {
val splitr = stream.spliterator();
return StreamPlus.from(
StreamSupport.stream(new Spliterators.AbstractSpliterator(splitr.estimateSize(), 0) {
boolean stillGoing = true;
@Override
public boolean tryAdvance(final Consumer super DATA> consumer) {
if (stillGoing) {
final boolean hadNext = splitr.tryAdvance(elem -> {
if (condition.test(elem)) {
consumer.accept(elem);
} else {
stillGoing = false;
}
});
return hadNext && stillGoing;
}
return false;
}
}, false)
);
});
}
public default StreamPlus takeUntil(Predicate super DATA> condition) {
return sequential(stream -> {
val splitr = stream.spliterator();
val resultStream = StreamSupport.stream(new Spliterators.AbstractSpliterator(splitr.estimateSize(), 0) {
boolean stillGoing = true;
@Override
public boolean tryAdvance(final Consumer super DATA> consumer) {
if (stillGoing) {
final boolean hadNext = splitr.tryAdvance(elem -> {
if (!condition.test(elem)) {
consumer.accept(elem);
} else {
stillGoing = false;
}
});
return hadNext && stillGoing;
}
return false;
}
}, false);
return StreamPlus.from(resultStream);
});
}
@Override
public default StreamPlus distinct() {
return deriveWith(stream -> {
return stream
.distinct();
});
}
//-- Sorted --
@Override
public default StreamPlus sorted() {
return deriveWith(stream -> {
return stream
.sorted();
});
}
@Override
public default StreamPlus sorted(
Comparator super DATA> comparator) {
return deriveWith(stream -> {
return (comparator == null)
? stream.sorted()
: stream.sorted(comparator);
});
}
public default > StreamPlus sortedBy(
Function super DATA, T> mapper) {
return deriveWith(stream -> {
return stream.sorted((a, b) -> {
T vA = mapper.apply(a);
T vB = mapper.apply(b);
return vA.compareTo(vB);
});
});
}
public default StreamPlus sortedBy(
Function super DATA, T> mapper,
Comparator comparator) {
return deriveWith(stream -> {
return stream.sorted((a, b) -> {
T vA = mapper.apply(a);
T vB = mapper.apply(b);
return Objects.compare(vA, vB, comparator);
});
});
}
//-- Terminate --
@Override
public default void forEach(
Consumer super DATA> action) {
terminate(stream -> {
if (action == null)
return;
stream
.forEach(action);
});
}
@Override
public default void forEachOrdered(
Consumer super DATA> action) {
terminate(stream -> {
if (action == null)
return;
stream
.forEachOrdered(action);
});
}
@Override
public default DATA reduce(
DATA identity,
BinaryOperator accumulator) {
return terminate(stream -> {
return stream
.reduce(identity, accumulator);
});
}
@Override
public default Optional reduce(
BinaryOperator accumulator) {
return terminate(stream -> {
return stream
.reduce(accumulator);
});
}
@Override
public default U reduce(
U identity,
BiFunction accumulator,
BinaryOperator combiner) {
return terminate(stream -> {
return stream
.reduce(identity, accumulator, combiner);
});
}
@Override
public default R collect(
Supplier supplier,
BiConsumer accumulator,
BiConsumer combiner) {
return terminate(stream -> {
return stream
.collect(supplier, accumulator, combiner);
});
}
@Override
public default R collect(
Collector super DATA, A, R> collector) {
return terminate(stream -> {
return stream
.collect(collector);
});
}
@Override
public default Optional min(
Comparator super DATA> comparator) {
return terminate(stream -> {
return stream
.min(comparator);
});
}
@Override
public default Optional max(
Comparator super DATA> comparator) {
return terminate(stream -> {
return stream
.max(comparator);
});
}
@Override
public default > Optional minBy(Func1 mapper) {
return min((a,b)->mapper.apply(a).compareTo(mapper.apply(b)));
}
@Override
public default > Optional maxBy(Func1 mapper) {
return max((a,b)->mapper.apply(a).compareTo(mapper.apply(b)));
}
@Override
public default long count() {
return terminate(stream -> {
return stream
.count();
});
}
public default int size() {
return terminate(stream -> {
return (int)stream
.count();
});
}
@Override
public default boolean anyMatch(
Predicate super DATA> predicate) {
return terminate(stream -> {
return stream
.anyMatch(predicate);
});
}
@Override
public default boolean allMatch(
Predicate super DATA> predicate) {
return terminate(stream -> {
return stream
.allMatch(predicate);
});
}
@Override
public default boolean noneMatch(
Predicate super DATA> predicate) {
return terminate(stream -> {
return stream
.noneMatch(predicate);
});
}
public default Optional findFirst() {
return stream().findFirst();
}
public default Optional findAny() {
return stream().findAny();
}
//== toXXX ===
@Override
public default Object[] toArray() {
return terminate(stream -> {
return stream
.toArray();
});
}
public default T[] toArray(T[] a) {
return terminate(stream -> {
T[] array
= toJavaList()
.toArray(a);
return array;
});
}
@Override
public default A[] toArray(IntFunction generator) {
return terminate(stream -> {
return stream
.toArray(generator);
});
}
public default List toJavaList() {
return terminate(stream -> {
return stream
.collect(Collectors.toList());
});
}
public default byte[] toByteArray(Func1 toByte) {
return terminate(stream -> {
val byteArray = new ByteArrayOutputStream();
stream.forEach(d -> byteArray.write(toByte.apply(d)));
return byteArray
.toByteArray();
});
}
public default int[] toIntArray(ToIntFunction toInt) {
return mapToInt(toInt)
.toArray();
}
public default long[] toLongArray(ToLongFunction toLong) {
return mapToLong(toLong)
.toArray();
}
public default double[] toDoubleArray(ToDoubleFunction toDouble) {
return mapToDouble(toDouble)
.toArray();
}
public default FuncList toList() {
return toImmutableList();
}
public default FuncList toFuncList() {
return toImmutableList();
}
public default String toListString() {
return "[" + map(String::valueOf).collect(Collectors.joining(", ")) + "]";
}
public default ImmutableList toImmutableList() {
return terminate(stream -> {
return ImmutableList.from(this);
});
}
public default List toMutableList() {
return toArrayList();
}
public default ArrayList toArrayList() {
return new ArrayList(toJavaList());
}
public default Set toSet() {
return new HashSet(this.collect(Collectors.toSet()));
}
//-- Iterator --
/** DO NOT USE THIS METHOD OR YOUR STREAM WILL NOT BE CLOSED. */
public default IteratorPlus __iterator() {
return IteratorPlus.from(stream());
}
@Override
public default IteratorPlus iterator() {
return terminate(s -> {
val iterator = __iterator();
return iterator;
});
}
@Override
public default Spliterator spliterator() {
return terminate(s -> {
val iterator = __iterator();
return Spliterators.spliteratorUnknownSize(iterator, 0);
});
}
/**
* Use iterator of this stream without terminating the stream.
*/
public default StreamPlus useIterator(Func1, StreamPlus> action) {
return sequential(stream -> {
StreamPlus result = null;
try {
val iterator = StreamPlus.from(stream).__iterator();
result = action.apply(iterator);
return result;
} finally {
if (result == null) {
f(()->close())
.runCarelessly();
} else {
result
.onClose(()->{
f(()->close())
.runCarelessly();
});
}
}
});
}
//== Plus ==
public default String joinToString() {
return terminate(stream -> {
return stream
.map(StrFuncs::toStr)
.collect(Collectors.joining());
});
}
public default String joinToString(String delimiter) {
return terminate(stream -> {
return stream
.map(StrFuncs::toStr)
.collect(Collectors.joining(delimiter));
});
}
//== Pipe ==
public default Pipeable extends StreamPlus> pipable() {
return Pipeable.of(this);
}
public default T pipeTo(Function super StreamPlus, T> piper) {
return piper.apply(this);
}
//== Spawn ==
/**
* Map each element to a uncompleted action, run them and collect which ever finish first.
* The result stream will not be the same order with the original one
* -- as stated, the order will be the order of completion.
* If the result StreamPlus is closed (which is done everytime a terminal operation is done),
* the unfinished actions will be canceled.
*/
public default StreamPlus> spawn(Func1> mapToAction) {
return sequential(stream -> {
val results = new ArrayList>();
val index = new AtomicInteger(0);
val actions
= stream()
.map (mapToAction)
.peek(action -> results.add(DeferAction.createNew()))
.peek(action -> action
.getPromise()
.onComplete(result -> {
val thisIndex = index.getAndIncrement();
val thisAction = results.get(thisIndex);
if (result.isValue())
thisAction.complete(result.value());
else thisAction.fail (result.exception());
})
)
.peek(action -> action.start())
.collect(Collectors.toList())
;
val resultStream
= StreamPlus
.from(results.stream().map(action -> action.getResult()))
;
resultStream
.onClose(()->actions.forEach(action -> action.abort("Stream closed!")));
return resultStream;
});
}
//== accumulate + restate ==
/**
* Accumulate the previous to the next element.
*
* For example:
* inputs = [i1, i2, i3, i4, i5, i6, i7, i8, i9, i10]
* and ~ is a accumulate function
*
* From this we get
* acc0 = head of inputs => i1
* rest0 = tail of inputs => [i2, i3, i4, i5, i6, i7, i8, i9, i10]
*
* The outputs are:
* output0 = acc0 with acc1 = acc0 ~ rest0 and rest1 = rest of rest0
* output1 = acc1 with acc2 = acc1 ~ rest1 and rest2 = rest of rest1
* output2 = acc2 with acc3 = acc2 ~ rest2 and rest3 = rest of rest2
* ...
*/
public default StreamPlus accumulate(BiFunction super DATA, ? super DATA, ? extends DATA> accumulator) {
return useIterator(iterator -> {
if (!iterator.hasNext())
return StreamPlus.empty();
val prev = new AtomicReference(iterator.next());
return StreamPlus
.concat(
StreamPlus.of(prev.get()),
iterator.stream().map(n -> {
val next = accumulator.apply(n, prev.get());
prev.set(next);
return next;
})
);
});
}
/**
* Use each of the element to recreate the stream by applying each element to the rest of the stream and repeat.
*
* For example:
* inputs = [i1, i2, i3, i4, i5, i6, i7, i8, i9, i10]
* and ~ is a restate function
*
* From this we get
* head0 = head of inputs = i1
* rest0 = tail of inputs = [i2, i3, i4, i5, i6, i7, i8, i9, i10]
*
* The outputs are:
* output0 = head0 with rest1 = head0 ~ rest0 and head1 = head of rest0
* output1 = head1 with rest2 = head1 ~ rest1 and head2 = head of rest2
* output2 = head2 with rest3 = head2 ~ rest2 and head3 = head of rest3
* ...
**/
@SuppressWarnings("unchecked")
public default StreamPlus restate(BiFunction super DATA, StreamPlus, StreamPlus> restater) {
val func = (UnaryOperator>>)((Tuple2> pair) -> {
val stream = pair._2();
if (stream == null)
return null;
Object[] head = new Object[] { null };
val iterator = stream.__iterator();
if (!iterator.hasNext())
return null;
head[0] = iterator.next();
val tail = restater.apply((DATA)head[0], iterator.stream());
if (tail == null)
return null;
return Tuple2.of((DATA)head[0], tail);
});
val seed = Tuple2.of((DATA)null, this);
val endStream
= iterate(seed, func)
.takeUntil(t -> t == null)
.skip(1)
.map(t -> t._1());
return endStream;
}
}