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

functionalj.list.FuncList Maven / Gradle / Ivy

// ============================================================================
// 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.list;
   
import static functionalj.function.Func.alwaysTrue;
import static functionalj.function.Func.themAll;
import static functionalj.lens.Access.$I;
import static java.util.function.Function.identity;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
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.stream.Collectors;
import java.util.stream.Stream;

import functionalj.function.Func1;
import functionalj.function.Func2;
import functionalj.function.Func3;
import functionalj.function.Func4;
import functionalj.function.Func5;
import functionalj.function.Func6;
import functionalj.lens.lenses.AnyLens;
import functionalj.map.FuncMap;
import functionalj.map.ImmutableMap;
import functionalj.pipeable.Pipeable;
import functionalj.promise.UncompletedAction;
import functionalj.result.Result;
import functionalj.stream.StreamPlus;
import functionalj.stream.StreamPlusHelper;
import functionalj.stream.Streamable;
import functionalj.stream.ZipWithOption;
import functionalj.tuple.IntTuple2;
import functionalj.tuple.Tuple;
import functionalj.tuple.Tuple2;
import functionalj.tuple.Tuple3;
import functionalj.tuple.Tuple4;
import functionalj.tuple.Tuple5;
import functionalj.tuple.Tuple6;
import lombok.val;

@SuppressWarnings("javadoc")
public interface FuncList
        extends ReadOnlyList, Streamable, Pipeable>, Predicate {
    
    public static  ImmutableList empty() {
        return ImmutableList.empty();
    }
    
    public static  ImmutableList emptyList() {
        return ImmutableList.empty();
    }
    
    public static  ImmutableList empty(Class elementClass) {
        return ImmutableList.empty();
    }
    
    public static  ImmutableList emptyList(Class elementClass) {
        return ImmutableList.empty();
    }
    
    @SafeVarargs
    public static  ImmutableList of(T... data) {
        return ImmutableList.of(data);
    }
    
    @SafeVarargs
    public static  ImmutableList AllOf(T... data) {
        return ImmutableList.of(data);
    }
    
    // TODO - Function to create FuncList from function of Array
    
    public static  ImmutableList from(T[] datas) {
        return ImmutableList.from(datas);
    }
    
    public static  ImmutableList from(Collection data) {
        return ImmutableList.from(data);
    }
    
    public static  ImmutableList from(List data) {
        return ImmutableList.from(data);
    }
    
    @SafeVarargs
    public static  ImmutableList ListOf(T... data) {
        return ImmutableList.of(data);
    }
    
    @SafeVarargs
    public static  ImmutableList listOf(T... data) {
        return ImmutableList.of(data);
    }
    
    public static  FuncList from(Streamable streamable) {
        return new FuncListDerived(streamable, identity());
    }
    
    public static  ImmutableList from(Stream stream) {
        return new ImmutableList(stream.collect(Collectors.toList()));
    }
    
    public static  ImmutableList from(ReadOnlyList readOnlyList) {
        return ImmutableList.from(readOnlyList);
    }
    
    public static  ImmutableList from(FuncList funcList) {
        return ImmutableList.from(funcList);
    }
    
    public static  FuncListBuilder newFuncList() {
        return new FuncListBuilder();
    }
    
    public static  FuncListBuilder newList() {
        return new FuncListBuilder();
    }
    
    public static  FuncListBuilder newBuilder() {
        return new FuncListBuilder();
    }
    
    // == Override ==
    
    @Override
    public default  FuncList deriveWith(Function, Stream> action) {
        val list = new FuncListDerived(this, action);
        val isLazy = isLazy();
        return isLazy ? list : new ImmutableList<>(list, false);
    }
    
    @Override
    public default  FuncList deriveFrom(Function, Stream> action) {
        return FuncListDerived.from((Supplier>) () -> {
            return action.apply(FuncList.this);
        });
    }
    
    @Override
    public default FuncList __data() throws Exception {
        return this;
    }
    
    @Override
    public default boolean test(DATA data) {
        return contains(data);
    }
    
    public default boolean isLazy() {
        return true;
    }
    
    public default boolean isEager() {
        return false;
    }
    
    public FuncList lazy();
    
    public FuncList eager();
    
    @Override
    public default List toJavaList() {
        return this;
    }
    
    public default FuncList toList() {
        return this;
    }
    
    public default ImmutableList freeze() {
        return toImmutableList();
    }
    
    // -- List specific --
    
    public default FuncList indexesOf(Predicate check) {
        return this.mapWithIndex((index, data) -> check.test(data) ? index : -1).filter($I.thatNotEqualsTo(-1))
                .toImmutableList();
    }
    
    @Override
    public default int indexOf(Object o) {
        return indexesOf(each -> Objects.equals(o, each)).findFirst().orElse(-1);
    }
    
    public default Optional first() {
        val valueRef = new AtomicReference();
        if (!StreamPlusHelper.hasAt(stream(), 0, valueRef))
            return Optional.empty();
    
        return Optional.ofNullable(valueRef.get());
    }
    
    public default FuncList first(int count) {
        val size  = size();
        val index = Math.max(0, size - count);
        return skip(index);
    }
    
    public default Optional last() {
        val size = this.size();
        if (size <= 0)
            return Optional.empty();
    
        return Optional.ofNullable(get(size - 1));
    }
    
    public default FuncList last(int count) {
        return limit(count);
    }
    
    public default Optional at(int index) {
        val ref = new AtomicReference();
        val found = StreamPlusHelper.hasAt(this.stream(), index, ref);
        if (!found)
            Optional.empty();
    
        return Optional.ofNullable(ref.get());
    }
    
    public default FuncList rest() {
        return deriveWith(stream -> stream.skip(1));
    }
    
    // Note - Eager
    public default FuncList reverse() {
        val temp = this.toMutableList();
        Collections.reverse(temp);
    
        val list = FuncList.from(temp);
        return isLazy() ? list.lazy() : list.eager();
    }
    
    // Note - Eager
    public default FuncList shuffle() {
        val temp = this.toMutableList();
        Collections.shuffle(temp);
    
        val list = FuncList.from(temp);
        return isLazy() ? list.lazy() : list.eager();
    }
    
    public default FuncList> query(Predicate check) {
        return this.mapWithIndex((index, data) -> check.test(data) ? new IntTuple2(index, data) : null)
                .filterNonNull();
    }
    
    public default > Optional minIndexBy(Func1 mapper) {
        return minIndexBy(alwaysTrue(), mapper);
    }
    
    public default > Optional maxIndexBy(Func1 mapper) {
        return maxIndexBy(alwaysTrue(), mapper);
    }
    
    public default > Optional minIndexBy(Predicate filter,
            Func1 mapper) {
        return stream().mapWithIndex(Tuple::of).filter(t -> filter.test(t._2)).minBy(t -> mapper.apply(t._2))
                .map(t -> t._1);
    }
    
    public default > Optional maxIndexBy(Predicate filter,
            Func1 mapper) {
        return stream().mapWithIndex(Tuple::of).filter(t -> filter.test(t._2)).maxBy(t -> mapper.apply(t._2))
                .map(t -> t._1);
    }
    
    // == Modified methods ==
    
    @Override
    public default Object[] toArray() {
        return stream().toArray();
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public default  T[] toArray(T[] a) {
        int count = size();
        if (a.length != count) {
            a = (T[])Array.newInstance(a.getClass().getComponentType(), count);
        }
        val array = a;
        forEachWithIndex((index, element) -> array[index] = (T)element);
        return array;
    }
    
    public default  A[] toArray(IntFunction generator) {
        return stream().toArray(generator);
    }
    
    public default FuncList append(DATA value) {
        return deriveWith(stream -> Stream.concat(stream, Stream.of(value)));
    }
    
    public default FuncList appendAll(DATA[] values) {
        return deriveWith(stream -> Stream.concat(stream, Stream.of(values)));
    }
    
    public default FuncList appendAll(Collection collection) {
        return ((collection == null) || collection.isEmpty()) ? this
                : deriveWith(stream -> Stream.concat(stream, collection.stream()));
    }
    
    public default FuncList appendAll(Supplier> supplier) {
        return (supplier == null) ? this : deriveWith(stream -> Stream.concat(stream, supplier.get()));
    }
    
    public default FuncList prepend(DATA value) {
        return deriveWith(stream -> Stream.concat(Stream.of(value), stream));
    }
    
    public default FuncList prependAll(DATA[] values) {
        return deriveWith(stream -> Stream.concat(Stream.of(values), stream));
    }
    
    public default FuncList prependAll(Collection collection) {
        return ((collection == null) || collection.isEmpty()) ? this
                : deriveWith(stream -> Stream.concat(collection.stream(), stream));
    }
    
    public default FuncList prependAll(Streamable streamable) {
        return (streamable == null) ? this : deriveWith(stream -> Stream.concat(streamable.stream(), stream));
    }
    
    public default FuncList prependAll(Supplier> supplier) {
        return (supplier == null) ? this : deriveWith(stream -> Stream.concat(supplier.get(), stream));
    }
    
    public default FuncList with(int index, DATA value) {
        if (index < 0)
            throw new IndexOutOfBoundsException(index + "");
        if (index >= size())
            throw new IndexOutOfBoundsException(index + " vs " + size());
    
        val i = new AtomicInteger();
        return deriveWith(stream -> stream.map(each -> (i.getAndIncrement() == index) ? value : each));
    }
    
    public default FuncList with(int index, Function mapper) {
        if (index < 0)
            throw new IndexOutOfBoundsException(index + "");
        if (index >= size())
            throw new IndexOutOfBoundsException(index + " vs " + size());
    
        val i = new AtomicInteger();
        return deriveWith(stream -> stream.map(each -> (i.getAndIncrement() == index) ? mapper.apply(each) : each));
    }
    
    @SuppressWarnings("unchecked")
    public default FuncList insertAt(int index, DATA... elements) {
        if ((elements == null) || (elements.length == 0))
            return this;
    
        return FuncListDerived.from(deriveFrom((Streamable streamable) -> {
            return Stream.concat(streamable.stream().limit(index),
                    Stream.concat(Stream.of(elements), streamable.stream().skip(index + 1)));
        }));
    }
    
    public default FuncList insertAllAt(int index, Collection collection) {
        if ((collection == null) || collection.isEmpty())
            return this;
    
        return FuncListDerived.from(deriveFrom((Streamable streamable) -> {
            return (Stream) Stream.concat(streamable.stream().limit(index),
                    Stream.concat(collection.stream(), streamable.stream().skip(index + 1)));
        }));
    }
    
    public default FuncList insertAllAt(int index, Streamable theStreamable) {
        if (theStreamable == null)
            return this;
    
        return FuncListDerived.from(deriveFrom((Streamable streamable) -> {
            return Stream.concat(streamable.stream().limit(index),
                    Stream.concat(theStreamable.stream(), streamable.stream().skip(index + 1)));
        }));
    }
    
    public default FuncList excludeAt(int index) {
        if (index < 0)
            throw new IndexOutOfBoundsException("index: " + index);
    
        return FuncListDerived.from(deriveFrom((Streamable streamable) -> {
            return Stream.concat(streamable.stream().limit(index), streamable.stream().skip(index + 2));
        }));
    }
    
    public default FuncList excludeFrom(int fromIndexInclusive, int count) {
        if (fromIndexInclusive < 0)
            throw new IndexOutOfBoundsException("fromIndexInclusive: " + fromIndexInclusive);
        if (count <= 0)
            throw new IndexOutOfBoundsException("count: " + count);
    
        return FuncListDerived.from(deriveFrom((Streamable streamable) -> {
            return Stream.concat(stream().limit(fromIndexInclusive), stream().skip(fromIndexInclusive + count));
        }));
    }
    
    public default FuncList excludeBetween(int fromIndexInclusive, int toIndexExclusive) {
        if (fromIndexInclusive < 0)
            throw new IndexOutOfBoundsException("fromIndexInclusive: " + fromIndexInclusive);
        if (toIndexExclusive < 0)
            throw new IndexOutOfBoundsException("toIndexExclusive: " + toIndexExclusive);
        if (fromIndexInclusive > toIndexExclusive)
            throw new IndexOutOfBoundsException(
                    "fromIndexInclusive: " + fromIndexInclusive + ", toIndexExclusive: " + toIndexExclusive);
        if (fromIndexInclusive == toIndexExclusive)
            return this;
    
        return FuncListDerived.from(deriveFrom((Streamable streamable) -> {
            return Stream.concat(stream().limit(fromIndexInclusive), stream().skip(toIndexExclusive + 1));
        }));
    }
    
    @Override
    public default FuncList subList(int fromIndexInclusive, int toIndexExclusive) {
        val length = toIndexExclusive - fromIndexInclusive;
        return new FuncListDerived<>(this, stream -> stream.skip(fromIndexInclusive).limit(length));
    }
    
    // ============================================================================
    // NOTE: The following part of the code was copied from StreamPlus
    // We will write a program to do the copy and replace ...
    // in the mean time, change this in StreamPlus.
    // ++ Plus w/ Self ++
    
    @Override
    public default FuncList sequential() {
        return deriveWith(stream -> {
            return stream.sequential();
        });
    }
    
    @Override
    public default FuncList parallel() {
        return deriveWith(stream -> {
            return stream.parallel();
        });
    }
    
    @Override
    public default FuncList unordered() {
        return deriveWith(stream -> {
            return stream.unordered();
        });
    }
    
    public default  FuncList map(Function mapper) {
        return deriveWith(stream -> {
            return stream.map(mapper);
        });
    }
    
    public default  FuncList flatMap(
            Function> mapper) {
        return deriveWith(stream -> {
            return stream.flatMap(e -> mapper.apply(e).stream());
        });
    }
    
    public default FuncList filter(Predicate predicate) {
        return deriveWith(stream -> {
            return (predicate == null) ? stream : stream.filter(predicate);
        });
    }
   
    public default FuncList peek(Consumer action) {
        return deriveWith(stream -> {
            return (action == null) ? stream : stream.peek(action);
        });
    }
    
    // -- Limit/Skip --
    
    @Override
    public default FuncList limit(long maxSize) {
        return deriveWith(stream -> {
            return stream.limit(maxSize);
        });
    }
    
    @Override
    public default FuncList skip(long n) {
        return deriveWith(stream -> {
            return stream.skip(n);
        });
    }
    
    public default FuncList skipWhile(Predicate condition) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).skipWhile(condition);
        });
    }
    
    public default FuncList skipUntil(Predicate condition) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).skipUntil(condition);
        });
    }
    
    public default FuncList takeWhile(Predicate condition) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).takeWhile(condition);
        });
    }
    
    public default FuncList takeUntil(Predicate condition) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).takeUntil(condition);
        });
    }
    
    @Override
    public default FuncList distinct() {
        return deriveWith(stream -> {
            return stream.distinct();
        });
    }
    
    @Override
    public default FuncList sorted() {
        return deriveWith(stream -> {
            return stream.sorted();
        });
    }
    
    @Override
    public default FuncList sorted(Comparator comparator) {
        return deriveWith(stream -> {
            return (comparator == null) ? stream.sorted() : stream.sorted(comparator);
        });
    }
    
    public default FuncList limit(Long maxSize) {
        return deriveWith(stream -> {
            return ((maxSize == null) || (maxSize.longValue() < 0)) ? stream : stream.limit(maxSize);
        });
    }
    
    public default FuncList skip(Long startAt) {
        return deriveWith(stream -> {
            return ((startAt == null) || (startAt.longValue() < 0)) ? stream : stream.skip(startAt);
        });
    }
    
    // -- Sorted --
    
    public default > FuncList 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  FuncList 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);
            });
        });
    }
    
    // -- fillNull --
    
    public default  FuncList fillNull(AnyLens lens, VALUE replacement) {
        return deriveWith(stream -> StreamPlus.from(stream).fillNull(lens, replacement));
    }
    
    public default  FuncList fillNull(Func1 get, Func2 set,
            VALUE replacement) {
        return deriveWith(stream -> StreamPlus.from(stream).fillNull(get, set, replacement));
    }
    
    public default  FuncList fillNull(AnyLens lens, Supplier replacementSupplier) {
        return deriveWith(stream -> StreamPlus.from(stream).fillNull(lens, replacementSupplier));
    }
    
    public default  FuncList fillNull(Func1 get, Func2 set,
            Supplier replacementSupplier) {
        return deriveWith(stream -> StreamPlus.from(stream).fillNull(get, set, replacementSupplier));
    }
    
    public default  FuncList fillNull(AnyLens lens, Func1 replacementFunction) {
        return deriveWith(stream -> StreamPlus.from(stream).fillNull(lens, replacementFunction));
    }
    
    public default  FuncList fillNull(Func1 get, Func2 set,
            Func1 replacementFunction) {
        return deriveWith(stream -> StreamPlus.from(stream).fillNull(get, set, replacementFunction));
    }
    
    // --map with condition --
    
    public default FuncList mapOnly(Predicate checker, Function mapper) {
        return map(d -> checker.test(d) ? mapper.apply(d) : d);
    }
   
    public default  FuncList mapIf(Predicate checker, Function mapper,
            Function elseMapper) {
        return deriveWith(stream -> StreamPlus.from(stream).mapIf(checker, mapper, elseMapper));
    }
    
    public default  FuncList mapFirst(Function mapper1, Function mapper2) {
        return deriveWith(stream -> StreamPlus.from(stream).mapFirst(mapper1, mapper2));
    }
    
    public default  FuncList mapFirst(Function mapper1, Function mapper2,
            Function mapper3) {
        return deriveWith(stream -> StreamPlus.from(stream).mapFirst(mapper1, mapper2, mapper3));
    }
    
    public default  FuncList mapFirst(Function mapper1, Function mapper2,
            Function mapper3, Function mapper4) {
        return deriveWith(stream -> StreamPlus.from(stream).mapFirst(mapper1, mapper2, mapper3, mapper4));
    }
    
    public default  FuncList mapFirst(Function mapper1, Function mapper2,
            Function mapper3, Function mapper4, Function mapper5) {
        return deriveWith(stream -> StreamPlus.from(stream).mapFirst(mapper1, mapper2, mapper3, mapper4, mapper5));
    }
    
    public default  FuncList mapFirst(Function mapper1, Function mapper2,
            Function mapper3, Function mapper4, Function mapper5,
            Function mapper6) {
        return deriveWith(
                stream -> StreamPlus.from(stream).mapFirst(mapper1, mapper2, mapper3, mapper4, mapper5, mapper6));
    }
    
    // -- mapWithIndex --
    
    public default FuncList> mapWithIndex() {
        val index = new AtomicInteger();
        return map(each -> Tuple2.of(index.getAndIncrement(), each));
    }
    
    public default  FuncList mapWithIndex(BiFunction mapper) {
        val index = new AtomicInteger();
        return map(each -> mapper.apply(index.getAndIncrement(), each));
    }
    
    public default  FuncList mapWithIndex(Function mapper1,
            BiFunction mapper) {
        return deriveWith(stream -> {
            val index = new AtomicInteger();
            return stream.map(each -> mapper.apply(index.getAndIncrement(), mapper1.apply(each)));
        });
    }
    
    // -- mapWithPrev --
    
    public default  FuncList mapWithPrev(
            BiFunction, ? super DATA, ? extends TARGET> mapper) {
        return deriveWith(stream -> {
            val prev = new AtomicReference>(Result.ofNotExist());
            return stream.map(element -> {
                val newValue = mapper.apply(prev.get(), element);
                prev.set(Result.valueOf(element));
                return newValue;
            });
        });
    }
    
    // -- accumulate --
    
    public default FuncList accumulate(BiFunction accumulator) {
        return deriveWith(stream -> {
            val iterator = StreamPlus.from(stream).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;
            }));
        });
    }
    
    // == Map to tuple. ==
    // ++ Generated with: GeneratorFunctorMapToTupleToObject ++
    
    public default  FuncList> mapTuple(Function mapper1,
            Function mapper2) {
        return mapThen(mapper1, mapper2, (v1, v2) -> Tuple2.of(v1, v2));
    }
    
    public default  FuncList> mapTuple(Function mapper1,
            Function mapper2, Function mapper3) {
        return mapThen(mapper1, mapper2, mapper3, (v1, v2, v3) -> Tuple3.of(v1, v2, v3));
    }
    
    public default  FuncList> mapTuple(
            Function mapper1, Function mapper2,
            Function mapper3, Function mapper4) {
        return mapThen(mapper1, mapper2, mapper3, mapper4, (v1, v2, v3, v4) -> Tuple4.of(v1, v2, v3, v4));
    }
    
    public default  FuncList> mapTuple(
            Function mapper1, Function mapper2,
            Function mapper3, Function mapper4,
            Function mapper5) {
        return mapThen(mapper1, mapper2, mapper3, mapper4, mapper5,
                (v1, v2, v3, v4, v5) -> Tuple5.of(v1, v2, v3, v4, v5));
    }
    
    public default  FuncList> mapTuple(
            Function mapper1, Function mapper2,
            Function mapper3, Function mapper4,
            Function mapper5, Function mapper6) {
        return mapThen(mapper1, mapper2, mapper3, mapper4, mapper5, mapper6,
                (v1, v2, v3, v4, v5, v6) -> Tuple6.of(v1, v2, v3, v4, v5, v6));
    }
    
    // -- Map and combine --
    
    public default  FuncList mapThen(Function mapper1,
            Function mapper2, BiFunction function) {
        return map(each -> {
            val v1 = mapper1.apply(each);
            val v2 = mapper2.apply(each);
            val v = function.apply(v1, v2);
            return v;
        });
    }
    
    public default  FuncList mapThen(Function mapper1,
            Function mapper2, Function mapper3,
            Func3 function) {
        return map(each -> {
            val v1 = mapper1.apply(each);
            val v2 = mapper2.apply(each);
            val v3 = mapper3.apply(each);
            val v = function.apply(v1, v2, v3);
            return v;
        });
    }
    
    public default  FuncList mapThen(Function mapper1,
            Function mapper2, Function mapper3,
            Function mapper4, Func4 function) {
        return map(each -> {
            val v1 = mapper1.apply(each);
            val v2 = mapper2.apply(each);
            val v3 = mapper3.apply(each);
            val v4 = mapper4.apply(each);
            val v = function.apply(v1, v2, v3, v4);
            return v;
        });
    }
    
    public default  FuncList mapThen(Function mapper1,
            Function mapper2, Function mapper3,
            Function mapper4, Function mapper5,
            Func5 function) {
        return map(each -> {
            val v1 = mapper1.apply(each);
            val v2 = mapper2.apply(each);
            val v3 = mapper3.apply(each);
            val v4 = mapper4.apply(each);
            val v5 = mapper5.apply(each);
            val v = function.apply(v1, v2, v3, v4, v5);
            return v;
        });
    }
    
    public default  FuncList mapThen(Function mapper1,
            Function mapper2, Function mapper3,
            Function mapper4, Function mapper5,
            Function mapper6, Func6 function) {
        return map(each -> {
            val v1 = mapper1.apply(each);
            val v2 = mapper2.apply(each);
            val v3 = mapper3.apply(each);
            val v4 = mapper4.apply(each);
            val v5 = mapper5.apply(each);
            val v6 = mapper6.apply(each);
            val v = function.apply(v1, v2, v3, v4, v5, v6);
            return v;
        });
    }
    
    // -- Generated with: GeneratorFunctorMapToTupleToObject --
    
    public default  FuncList> mapToMap(KEY key,
            Function mapper) {
        return map(data -> ImmutableMap.of(key, mapper.apply(data)));
    }
    
    public default  FuncList> mapToMap(KEY key1,
            Function mapper1, KEY key2,
            Function mapper2) {
        return map(data -> ImmutableMap.of(key1, mapper1.apply(data), key2, mapper2.apply(data)));
    }
    
    public default  FuncList> mapToMap(KEY key1,
            Function mapper1, KEY key2, Function mapper2,
            KEY key3, Function mapper3) {
        return map(data -> ImmutableMap.of(key1, mapper1.apply(data), key2, mapper2.apply(data), key3,
                mapper3.apply(data)));
    }
    
    public default  FuncList> mapToMap(KEY key1,
            Function mapper1, KEY key2, Function mapper2,
            KEY key3, Function mapper3, KEY key4,
            Function mapper4) {
        return map(data -> ImmutableMap.of(key1, mapper1.apply(data), key2, mapper2.apply(data), key3,
                mapper3.apply(data), key4, mapper4.apply(data)));
    }
    
    public default  FuncList> mapToMap(KEY key1,
            Function mapper1, KEY key2, Function mapper2,
            KEY key3, Function mapper3, KEY key4,
            Function mapper4, KEY key5,
            Function mapper5) {
        return map(data -> ImmutableMap.of(key1, mapper1.apply(data), key2, mapper2.apply(data), key3,
                mapper3.apply(data), key4, mapper4.apply(data), key5, mapper5.apply(data)));
    }
    
    public default  FuncList> mapToMap(KEY key1,
            Function mapper1, KEY key2, Function mapper2,
            KEY key3, Function mapper3, KEY key4,
            Function mapper4, KEY key5, Function mapper5,
            KEY key6, Function mapper6) {
        return map(data -> ImmutableMap.of(key1, mapper1.apply(data), key2, mapper2.apply(data), key3,
                mapper3.apply(data), key4, mapper4.apply(data), key5, mapper5.apply(data), key6, mapper6.apply(data)));
    }
    
    public default  FuncList> mapToMap(KEY key1,
            Function mapper1, KEY key2, Function mapper2,
            KEY key3, Function mapper3, KEY key4,
            Function mapper4, KEY key5, Function mapper5,
            KEY key6, Function mapper6, KEY key7,
            Function mapper7) {
        return map(data -> ImmutableMap.of(key1, mapper1.apply(data), key2, mapper2.apply(data), key3,
                mapper3.apply(data), key4, mapper4.apply(data), key5, mapper5.apply(data), key6, mapper6.apply(data),
                key7, mapper7.apply(data)));
    }
    
    public default  FuncList> mapToMap(KEY key1,
            Function mapper1, KEY key2, Function mapper2,
            KEY key3, Function mapper3, KEY key4,
            Function mapper4, KEY key5, Function mapper5,
            KEY key6, Function mapper6, KEY key7,
            Function mapper7, KEY key8,
            Function mapper8) {
        return map(data -> ImmutableMap.of(key1, mapper1.apply(data), key2, mapper2.apply(data), key3,
                mapper3.apply(data), key4, mapper4.apply(data), key5, mapper5.apply(data), key6, mapper6.apply(data),
                key7, mapper7.apply(data), key8, mapper8.apply(data)));
    }
    
    public default  FuncList> mapToMap(KEY key1,
            Function mapper1, KEY key2, Function mapper2,
            KEY key3, Function mapper3, KEY key4,
            Function mapper4, KEY key5, Function mapper5,
            KEY key6, Function mapper6, KEY key7,
            Function mapper7, KEY key8, Function mapper8,
            KEY key9, Function mapper9) {
        return map(data -> ImmutableMap.of(key1, mapper1.apply(data), key2, mapper2.apply(data), key3,
                mapper3.apply(data), key4, mapper4.apply(data), key5, mapper5.apply(data), key6, mapper6.apply(data),
                key7, mapper7.apply(data), key8, mapper8.apply(data), key9, mapper9.apply(data)));
    }
    
    public default  FuncList> mapToMap(KEY key1,
            Function mapper1, KEY key2, Function mapper2,
            KEY key3, Function mapper3, KEY key4,
            Function mapper4, KEY key5, Function mapper5,
            KEY key6, Function mapper6, KEY key7,
            Function mapper7, KEY key8, Function mapper8,
            KEY key9, Function mapper9, KEY key10,
            Function mapper10) {
        return map(data -> ImmutableMap.of(key1, mapper1.apply(data), key2, mapper2.apply(data), key3,
                mapper3.apply(data), key4, mapper4.apply(data), key5, mapper5.apply(data), key6, mapper6.apply(data),
                key7, mapper7.apply(data), key8, mapper8.apply(data), key9, mapper9.apply(data), key10,
                mapper10.apply(data)));
    }
    
    // -- Filter --
    
    public default FuncList filterNonNull() {
        return deriveWith(stream -> stream.filter(Objects::nonNull));
    }
    
    public default FuncList filterIn(Collection collection) {
        return deriveWith(stream -> {
            return (collection == null) ? Stream.empty() : stream.filter(data -> collection.contains(data));
        });
    }
    
    public default FuncList exclude(Predicate predicate) {
        return deriveWith(stream -> {
            return (predicate == null) ? stream : stream.filter(data -> !predicate.test(data));
        });
    }
    
    public default FuncList excludeIn(Collection collection) {
        return deriveWith(stream -> {
            return (collection == null) ? stream : stream.filter(data -> !collection.contains(data));
        });
    }
    
    public default FuncList exclude(DATA value) {
        return deriveWith(stream -> {
            return stream.filter(data -> !Objects.equals(value, data));
        });
    }
    
    public default FuncList excludeAll(DATA[] datas) {
        val dataList = FuncList.of(datas);
        return deriveWith(stream -> {
            return (datas == null) ? stream : stream.filter(data -> !dataList.contains(data));
        });
    }
    
    public default  FuncList filter(Class clzz) {
        return filter(clzz::isInstance);
    }
    
    public default  FuncList filter(Class clzz, Predicate theCondition) {
        return filter(value -> {
            if (!clzz.isInstance(value))
                return false;
    
            val target = clzz.cast(value);
            val isPass = theCondition.test(target);
            return isPass;
        });
    }
    
    public default  FuncList filter(Function mapper, Predicate theCondition) {
        return filter(value -> {
            val target = mapper.apply(value);
            val isPass = theCondition.test(target);
            return isPass;
        });
    }
    
    public default FuncList filterWithIndex(BiFunction predicate) {
        val index = new AtomicInteger();
        return filter(each -> {
            return (predicate != null) && predicate.apply(index.getAndIncrement(), each);
        });
    }
    
    // -- Peek --
    
    public default  FuncList peek(Class clzz, Consumer theConsumer) {
        return peek(value -> {
            if (!clzz.isInstance(value))
                return;
    
            val target = clzz.cast(value);
            theConsumer.accept(target);
        });
    }
    
    public default FuncList peek(Predicate selector, Consumer theConsumer) {
        return peek(value -> {
            if (!selector.test(value))
                return;
    
            theConsumer.accept(value);
        });
    }
    
    public default  FuncList peek(Function mapper, Consumer theConsumer) {
        return peek(value -> {
            val target = mapper.apply(value);
            theConsumer.accept(target);
        });
    }
    
    public default  FuncList peek(Function mapper, Predicate selector,
            Consumer theConsumer) {
        return peek(value -> {
            val target = mapper.apply(value);
            if (selector.test(target))
                theConsumer.accept(target);
        });
    }
    
    // -- FlatMap --
    
    public default FuncList flatMapOnly(
            Predicate                            checker,
            Function> mapper) {
        return flatMap(d -> checker.test(d) ? mapper.apply(d) : () -> StreamPlus.of(d));
    }
    
    public default  FuncList flatMapIf(
            Predicate                         checker,
            Function> mapper, 
            Function> elseMapper) {
        return flatMap(d -> checker.test(d) ? mapper.apply(d) : elseMapper.apply(d));
    }
    
    // -- segment --
    
    public default FuncList> segment(int count) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).segment(count);
        });
    }
    
    public default FuncList> segment(int count, boolean includeTail) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).segment(count, includeTail);
        });
    }
    
    public default FuncList> segment(Predicate startCondition) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).segment(startCondition);
        });
    }
    
    public default FuncList> segment(Predicate startCondition, boolean includeTail) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).segment(startCondition);
        });
    }
    
    public default FuncList> segment(Predicate startCondition, Predicate endCondition) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).segment(startCondition, endCondition);
        });
    }
    
    public default FuncList> segment(Predicate startCondition, Predicate endCondition,
            boolean includeLast) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).segment(startCondition, endCondition, includeLast);
        });
    }
    
    // -- Zip --
    
    public default  FuncList combineWith(Stream anotherStream, Func2 combinator) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).combineWith(anotherStream, combinator);
        });
    }
    
    public default  FuncList combineWith(Stream anotherStream, ZipWithOption option,
            Func2 combinator) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).combineWith(anotherStream, option, combinator);
        });
    }
    
    public default  FuncList> zipWith(Stream anotherStream) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).zipWith(anotherStream);
        });
    }
    
    public default  FuncList> zipWith(Stream anotherStream, ZipWithOption option) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).zipWith(anotherStream, option);
        });
    }
    
    public default FuncList choose(Stream anotherStream, Func2 selectThisNotAnother) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).choose(anotherStream, selectThisNotAnother);
        });
    }
    
    public default FuncList merge(Stream anotherStream) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream).merge(anotherStream);
        });
    }
    
    @SuppressWarnings("unchecked")
    public default FuncList concatWith(FuncList ... tails) {
        return deriveWith(stream -> {
            return StreamPlus
                    .concat(StreamPlus.of(stream), StreamPlus.of(tails).map(Streamable::stream))
                    .flatMap(themAll());
        });
    }
    
    // TODO Join
    
    // -- Plus w/ Self --
    // ============================================================================
    
    public default FuncList collapse(Predicate conditionToCollapse, Func2 concatFunc) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream()).collapse(conditionToCollapse, concatFunc);
        });
    }
    
    public default  Streamable> spawn(Func1> mapper) {
        return deriveWith(stream -> {
            return StreamPlus.from(stream()).spawn(mapper);
        });
    }
}