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

functionalj.stream.StreamPlus Maven / Gradle / Ivy

There is a newer version: 1.0.17
Show newest version
// ============================================================================
// 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 mapper) {
        val intStreamPlus = IntStreamPlus.from(stream().mapToInt(mapper));
        intStreamPlus.onClose(()->{
            close();
        });
        return intStreamPlus;
    }
    
    @Override
    public default LongStreamPlus mapToLong(
            ToLongFunction mapper) {
        return LongStreamPlus
                .from(
                    stream()
                    .mapToLong(mapper));
    }
    
    @Override
    public default DoubleStreamPlus mapToDouble(
            ToDoubleFunction mapper) {
        return DoubleStreamPlus
                .from(
                    stream()
                    .mapToDouble(mapper));
    }
    
    @Override
    public default IntStreamPlus flatMapToInt(
            Function mapper) {
        return IntStreamPlus
                .from(
                    stream()
                    .flatMapToInt(mapper));
    }
    
    @Override
    public default LongStreamPlus flatMapToLong(
            Function mapper) {
        return LongStreamPlus
                .from(
                    stream()
                    .flatMapToLong(mapper));
    }
    
    @Override
    public default DoubleStreamPlus flatMapToDouble(
            Function mapper) {
        return DoubleStreamPlus
                .from(
                    stream()
                    .flatMapToDouble(mapper));
    }
    
    @Override
    public default  StreamPlus map(
            Function mapper) {
        return deriveWith(stream -> {
            return stream
                    .map(mapper);
        });
    }
    
    @Override
    public default  StreamPlus flatMap(
            Function> mapper) {
        return deriveWith(stream -> {
            return stream
                    .flatMap(mapper);
        });
    }
    
    @Override
    public default StreamPlus filter(
            Predicate predicate) {
        return deriveWith(stream -> {
            return (predicate == null)
                ? stream
                : stream.filter(predicate);
        });
    }
    
    @Override
    public default  StreamPlus filter(
            Function mapper, 
            Predicate      theCondition) {
        return filter(value -> {
            val target = mapper.apply(value);
            val isPass = theCondition.test(target);
            return isPass;
        });
    }
    
    @Override
    public default StreamPlus peek(
            Consumer 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 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 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 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 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 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 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 comparator) {
        return deriveWith(stream -> {
            return (comparator == null)
                    ? stream.sorted()
                    : stream.sorted(comparator);
        });
    }
    
    public default > StreamPlus sortedBy(
            Function 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 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 action) {
        terminate(stream -> {
            if (action == null)
                return;
            
            stream
            .forEach(action);
        });
    }
    
    @Override
    public default void forEachOrdered(
            Consumer 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 collector) {
        return terminate(stream -> {
            return stream
                    .collect(collector);
        });
    }
    
    @Override
    public default Optional min(
            Comparator comparator) {
        return terminate(stream -> {
            return stream
                    .min(comparator);
        });
    }
    
    @Override
    public default Optional max(
            Comparator 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 predicate) {
        return terminate(stream -> {
            return stream
                    .anyMatch(predicate);
        });
    }
    
    @Override
    public default boolean allMatch(
            Predicate predicate) {
        return terminate(stream -> {
            return stream
                    .allMatch(predicate);
        });
    }
    
    @Override
    public default boolean noneMatch(
            Predicate 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> pipable() {
        return Pipeable.of(this);
    }
    
    public default  T pipeTo(Function, 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 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, 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;
    }
    
}