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

com.landawn.abacus.util.stream.AbstractStream Maven / Gradle / Ivy

/*
 * Copyright (C) 2016 HaiYang Li
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.landawn.abacus.util.stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import com.landawn.abacus.DataSet;
import com.landawn.abacus.exception.UncheckedIOException;
import com.landawn.abacus.exception.UncheckedSQLException;
import com.landawn.abacus.util.Array;
import com.landawn.abacus.util.BufferedWriter;
import com.landawn.abacus.util.ByteIterator;
import com.landawn.abacus.util.ByteSummaryStatistics;
import com.landawn.abacus.util.CharIterator;
import com.landawn.abacus.util.CharSummaryStatistics;
import com.landawn.abacus.util.DoubleIterator;
import com.landawn.abacus.util.DoubleSummaryStatistics;
import com.landawn.abacus.util.FloatIterator;
import com.landawn.abacus.util.FloatSummaryStatistics;
import com.landawn.abacus.util.Fn;
import com.landawn.abacus.util.IOUtil;
import com.landawn.abacus.util.Indexed;
import com.landawn.abacus.util.IntIterator;
import com.landawn.abacus.util.IntSummaryStatistics;
import com.landawn.abacus.util.ListMultimap;
import com.landawn.abacus.util.LongIterator;
import com.landawn.abacus.util.LongSummaryStatistics;
import com.landawn.abacus.util.Matrix;
import com.landawn.abacus.util.Multimap;
import com.landawn.abacus.util.Multiset;
import com.landawn.abacus.util.MutableBoolean;
import com.landawn.abacus.util.MutableLong;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.Nth;
import com.landawn.abacus.util.NullabLe;
import com.landawn.abacus.util.ObjectFactory;
import com.landawn.abacus.util.Optional;
import com.landawn.abacus.util.OptionalDouble;
import com.landawn.abacus.util.Pair;
import com.landawn.abacus.util.Percentage;
import com.landawn.abacus.util.PermutationIterator;
import com.landawn.abacus.util.Seq;
import com.landawn.abacus.util.ShortIterator;
import com.landawn.abacus.util.ShortSummaryStatistics;
import com.landawn.abacus.util.Try;
import com.landawn.abacus.util.function.BiConsumer;
import com.landawn.abacus.util.function.BiFunction;
import com.landawn.abacus.util.function.BiPredicate;
import com.landawn.abacus.util.function.BinaryOperator;
import com.landawn.abacus.util.function.Consumer;
import com.landawn.abacus.util.function.Function;
import com.landawn.abacus.util.function.IntFunction;
import com.landawn.abacus.util.function.Predicate;
import com.landawn.abacus.util.function.Supplier;
import com.landawn.abacus.util.function.ToByteFunction;
import com.landawn.abacus.util.function.ToCharFunction;
import com.landawn.abacus.util.function.ToDoubleFunction;
import com.landawn.abacus.util.function.ToFloatFunction;
import com.landawn.abacus.util.function.ToIntFunction;
import com.landawn.abacus.util.function.ToLongFunction;
import com.landawn.abacus.util.function.ToShortFunction;
import com.landawn.abacus.util.function.TriConsumer;
import com.landawn.abacus.util.function.TriFunction;

/**
 * This class is a sequential, stateful and immutable stream implementation.
 *
 * @param 
 * @since 0.8
 * 
 * @author Haiyang Li
 */
abstract class AbstractStream extends Stream {

    AbstractStream(final Collection closeHandlers, final boolean sorted, final Comparator cmp) {
        super(closeHandlers, sorted, cmp);
    }

    @Override
    public  Stream filter(final U seed, final BiPredicate predicate) {
        return filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return predicate.test(value, seed);
            }
        });
    }

    @Override
    public  Stream takeWhile(final U seed, final BiPredicate predicate) {
        return takeWhile(new Predicate() {
            @Override
            public boolean test(T value) {
                return predicate.test(value, seed);
            }
        });
    }

    @Override
    public  Stream dropWhile(final U seed, final BiPredicate predicate) {
        return dropWhile(new Predicate() {
            @Override
            public boolean test(T value) {
                return predicate.test(value, seed);
            }
        });
    }

    @Override
    public Stream remove(final long n, final Consumer action) {
        if (n < 0) {
            throw new IllegalArgumentException("'n' can't be less than 0");
        } else if (n == 0) {
            return this;
        }

        if (this.isParallel()) {
            final AtomicLong cnt = new AtomicLong(n);

            return removeWhile(new Predicate() {
                @Override
                public boolean test(T value) {
                    return cnt.getAndDecrement() > 0;
                }
            }, action);
        } else {
            final MutableLong cnt = MutableLong.of(n);

            return removeWhile(new Predicate() {

                @Override
                public boolean test(T value) {
                    return cnt.getAndDecrement() > 0;
                }

            }, action);
        }
    }

    @Override
    public Stream removeIf(final Predicate predicate) {
        N.requireNonNull(predicate);

        return filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return predicate.test(value) == false;
            }
        });
    }

    @Override
    public  Stream removeIf(final U seed, final BiPredicate predicate) {
        N.requireNonNull(predicate);

        return filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return predicate.test(value, seed) == false;
            }
        });
    }

    @Override
    public Stream removeIf(final Predicate predicate, final Consumer action) {
        N.requireNonNull(predicate);
        N.requireNonNull(action);

        return filter(new Predicate() {
            @Override
            public boolean test(T value) {
                if (predicate.test(value)) {
                    action.accept(value);
                    return false;
                }

                return true;
            }
        });
    }

    @Override
    public  Stream removeIf(final U seed, final BiPredicate predicate, final Consumer action) {
        N.requireNonNull(predicate);
        N.requireNonNull(action);

        return filter(new Predicate() {
            @Override
            public boolean test(T value) {
                if (predicate.test(value, seed)) {
                    action.accept(value);
                    return false;
                }

                return true;
            }
        });
    }

    @Override
    public Stream removeWhile(final Predicate predicate, final Consumer action) {
        N.requireNonNull(predicate);
        N.requireNonNull(action);

        return dropWhile(new Predicate() {
            @Override
            public boolean test(T value) {
                if (predicate.test(value)) {
                    action.accept(value);
                    return true;
                }

                return false;
            }
        });
    }

    @Override
    public  Stream removeWhile(final U seed, final BiPredicate predicate, final Consumer action) {
        N.requireNonNull(predicate);
        N.requireNonNull(action);

        return dropWhile(new Predicate() {
            @Override
            public boolean test(T value) {
                if (predicate.test(value, seed)) {
                    action.accept(value);
                    return true;
                }

                return false;
            }
        });
    }

    @Override
    public Stream step(final long step) {
        N.checkArgument(step > 0, "'step' can't be 0 or negative: %s", step);

        if (step == 1) {
            return this;
        }

        final long skip = step - 1;
        final ExIterator iter = exIterator();

        final Iterator iterator = new ExIterator() {
            @Override
            public boolean hasNext() {
                return iter.hasNext();
            }

            @Override
            public T next() {
                final T next = iter.next();
                iter.skip(skip);
                return next;
            }
        };

        return newStream(iterator, sorted, cmp);
    }

    @Override
    public  Stream map(final U seed, final BiFunction mapper) {
        return map(new Function() {
            @Override
            public R apply(T t) {
                return mapper.apply(t, seed);
            }
        });
    }

    @Override
    public  Stream biMap(BiFunction mapper) {
        return biMap(mapper, false);
    }

    @Override
    public  Stream triMap(TriFunction mapper) {
        return triMap(mapper, false);
    }

    @Override
    public  Stream slidingMap(BiFunction mapper) {
        return slidingMap(mapper, 1);
    }

    @Override
    public  Stream slidingMap(TriFunction mapper) {
        return slidingMap(mapper, 1);
    }

    @Override
    public  Stream rangeMap(final BiPredicate sameRange, final BiFunction mapper) {
        final Iterator iter = iterator();

        return newStream(new ExIterator() {
            private final T NULL = (T) Stream.NONE;
            private T next = NULL;

            @Override
            public boolean hasNext() {
                return next != NULL || iter.hasNext();
            }

            @Override
            public U next() {
                final T left = next == NULL ? iter.next() : next;
                T right = left;

                while (iter.hasNext() && sameRange.test(right, (next = iter.next()))) {
                    right = next;
                }

                if (right == next) {
                    next = NULL;
                }

                return mapper.apply(left, right);
            }
        }, false, null);
    }

    abstract  Stream flatMap0(final Function> mapper);

    //    @Override
    //    public  EntryStream mapToEntry() {
    //        return null;
    //    }

    @Override
    public  EntryStream mapToEntry(final Function> mapper) {
        final Function mapper2 = Fn.identity();

        if (mapper == mapper2) {
            return EntryStream.of((Stream>) this);
        }

        return EntryStream.of(map(mapper));
    }

    @Override
    public  EntryStream mapToEntry(final Function keyMapper, final Function valueMapper) {
        final Function> mapper = new Function>() {
            @Override
            public Entry apply(T t) {
                return new AbstractMap.SimpleImmutableEntry<>(keyMapper.apply(t), valueMapper.apply(t));
            }
        };

        return mapToEntry(mapper);
    }

    @Override
    public  Stream flatMap(final Function> mapper) {
        return flatMap0(new Function>() {
            @Override
            public Iterator apply(T t) {
                return mapper.apply(t).iterator();
            }
        });
    }

    @Override
    public  Stream flatMap(final U seed, final BiFunction> mapper) {
        return flatMap(new Function>() {
            @Override
            public Stream apply(T t) {
                return mapper.apply(t, seed);
            }
        });
    }

    @Override
    public  Stream flatCollection(final Function> mapper) {
        return flatMap0(new Function>() {
            @Override
            public Iterator apply(T t) {
                return mapper.apply(t).iterator();
            }
        });
    }

    @Override
    public  Stream flatCollection(final U seed, final BiFunction> mapper) {
        return flatCollection(new Function>() {
            @Override
            public Collection apply(T t) {
                return mapper.apply(t, seed);
            }
        });
    }

    @Override
    public  Stream flatArray(final Function mapper) {
        return flatMap0(new Function>() {
            @Override
            public Iterator apply(T t) {
                return ExIterator.of(mapper.apply(t));
            }
        });
    }

    @Override
    public  Stream flatArray(final U seed, final BiFunction mapper) {
        return flatArray(new Function() {
            @Override
            public R[] apply(T t) {
                return mapper.apply(t, seed);
            }
        });
    }

    @Override
    public CharStream flatMapToChar(final Function mapper) {
        return flatMapToChar0(new Function() {
            @Override
            public CharIterator apply(T t) {
                return mapper.apply(t).exIterator();
            }
        });
    }

    abstract CharStream flatMapToChar0(Function function);

    @Override
    public ByteStream flatMapToByte(final Function mapper) {
        return flatMapToByte0(new Function() {
            @Override
            public ByteIterator apply(T t) {
                return mapper.apply(t).exIterator();
            }
        });
    }

    abstract ByteStream flatMapToByte0(Function function);

    @Override
    public ShortStream flatMapToShort(final Function mapper) {
        return flatMapToShort0(new Function() {
            @Override
            public ShortIterator apply(T t) {
                return mapper.apply(t).exIterator();
            }
        });
    }

    abstract ShortStream flatMapToShort0(Function function);

    @Override
    public IntStream flatMapToInt(final Function mapper) {
        return flatMapToInt0(new Function() {
            @Override
            public IntIterator apply(T t) {
                return mapper.apply(t).exIterator();
            }
        });
    }

    abstract IntStream flatMapToInt0(Function function);

    @Override
    public LongStream flatMapToLong(final Function mapper) {
        return flatMapToLong0(new Function() {
            @Override
            public LongIterator apply(T t) {
                return mapper.apply(t).exIterator();
            }
        });
    }

    abstract LongStream flatMapToLong0(Function function);

    @Override
    public FloatStream flatMapToFloat(final Function mapper) {
        return flatMapToFloat0(new Function() {
            @Override
            public FloatIterator apply(T t) {
                return mapper.apply(t).exIterator();
            }
        });
    }

    abstract FloatStream flatMapToFloat0(Function function);

    @Override
    public DoubleStream flatMapToDouble(final Function mapper) {
        return flatMapToDouble0(new Function() {
            @Override
            public DoubleIterator apply(T t) {
                return mapper.apply(t).exIterator();
            }
        });
    }

    abstract DoubleStream flatMapToDouble0(Function function);

    @Override
    public  EntryStream flatMapToEntry(final Function>> mapper) {
        return EntryStream.of(flatMap(mapper));
    }

    @Override
    @SuppressWarnings("rawtypes")
    public Stream sortedBy(final Function keyExtractor) {
        final Comparator comparator = new Comparator() {
            @Override
            public int compare(T o1, T o2) {
                return N.compare(keyExtractor.apply(o1), keyExtractor.apply(o2));
            }
        };

        return sorted(comparator);
    }

    @Override
    public Stream> split(final int size) {
        return splitToList(size).map(new Function, Stream>() {
            @Override
            public Stream apply(List t) {
                return new ArrayStream<>(toArray(t), 0, t.size(), null, sorted, cmp);
            }
        });
    }

    @Override
    public Stream> split(final Predicate predicate) {
        return splitToList(predicate).map(new Function, Stream>() {
            @Override
            public Stream apply(List t) {
                return new ArrayStream<>(toArray(t), 0, t.size(), null, sorted, cmp);
            }
        });
    }

    @Override
    public Stream> splitToList(final Predicate predicate) {
        final BiFunction predicate2 = new BiFunction() {

            @Override
            public Boolean apply(T t, Object u) {
                return predicate.test(t);
            }
        };

        return splitToList(null, predicate2, null);
    }

    @Override
    public  Stream> split(final U identity, final BiFunction predicate, final Consumer identityUpdate) {
        return splitToList(identity, predicate, identityUpdate).map(new Function, Stream>() {
            @Override
            public Stream apply(List t) {
                return new ArrayStream<>(toArray(t), 0, t.size(), null, sorted, cmp);
            }
        });
    }

    @Override
    public Stream> sliding(final int windowSize, final int increment) {
        return slidingToList(windowSize, increment).map(new Function, Stream>() {
            @Override
            public Stream apply(List t) {
                return new ArrayStream<>(toArray(t), 0, t.size(), null, sorted, cmp);
            }
        });
    }

    @Override
    public Stream collapse(final BiPredicate collapsible, final BiFunction mergeFunction) {
        final ExIterator iter = exIterator();

        return this.newStream(new ExIterator() {
            private boolean hasNext = false;
            private T next = null;

            @Override
            public boolean hasNext() {
                return hasNext || iter.hasNext();
            }

            @Override
            public T next() {
                T res = hasNext ? next : (next = iter.next());

                while ((hasNext = iter.hasNext())) {
                    if (collapsible.test(next, (next = iter.next()))) {
                        res = mergeFunction.apply(res, next);
                    } else {
                        break;
                    }
                }

                return res;
            }
        }, false, null);
    }

    @Override
    public  Stream collapse(final BiPredicate collapsible, final Collector collector) {
        final Supplier supplier = collector.supplier();
        final BiConsumer accumulator = collector.accumulator();
        final Function finisher = collector.finisher();
        final ExIterator iter = exIterator();

        return this.newStream(new ExIterator() {
            private boolean hasNext = false;
            private T next = null;

            @Override
            public boolean hasNext() {
                return hasNext || iter.hasNext();
            }

            @Override
            public R next() {
                final A c = supplier.get();
                accumulator.accept(c, hasNext ? next : (next = iter.next()));

                while ((hasNext = iter.hasNext())) {
                    if (collapsible.test(next, (next = iter.next()))) {
                        accumulator.accept(c, next);
                    } else {
                        break;
                    }
                }

                return finisher.apply(c);
            }
        }, false, null);
    }

    @Override
    public Stream scan(final BiFunction accumulator) {
        final ExIterator iter = exIterator();

        return this.newStream(new ExIterator() {
            private T res = null;
            private boolean isFirst = true;

            @Override
            public boolean hasNext() {
                return iter.hasNext();
            }

            @Override
            public T next() {
                if (isFirst) {
                    isFirst = false;
                    return (res = iter.next());
                } else {
                    return (res = accumulator.apply(res, iter.next()));
                }
            }
        }, false, null);
    }

    @Override
    public  Stream scan(final R seed, final BiFunction accumulator) {
        final ExIterator iter = exIterator();

        return this.newStream(new ExIterator() {
            private R res = seed;

            @Override
            public boolean hasNext() {
                return iter.hasNext();
            }

            @Override
            public R next() {
                return (res = accumulator.apply(res, iter.next()));
            }
        }, false, null);
    }

    @Override
    public Stream intersperse(final T delimiter) {
        return newStream(new ExIterator() {
            private final Iterator iter = iterator();
            private boolean toInsert = false;

            @Override
            public boolean hasNext() {
                return iter.hasNext();
            }

            @Override
            public T next() {
                if (hasNext() == false) {
                    throw new NoSuchElementException();
                }

                if (toInsert) {
                    toInsert = false;
                    return delimiter;
                } else {
                    final T res = iter.next();
                    toInsert = true;
                    return res;
                }
            }
        }, false, null);
    }

    @Override
    public void forEachPair(final BiConsumer action) {
        forEachPair(action, 1);
    }

    @Override
    public void forEachTriple(final TriConsumer action) {
        forEachTriple(action, 1);
    }

    @Override
    public  Stream>> groupBy(final Function classifier) {
        final Map> map = collect(Collectors.groupingBy(classifier));

        return newStream(map.entrySet().iterator(), false, null);
    }

    @Override
    public  Stream>> groupBy(final Function classifier, Supplier>> mapFactory) {
        final Map> map = collect(Collectors.groupingBy(classifier, mapFactory));

        return newStream(map.entrySet().iterator(), false, null);
    }

    @Override
    public  Stream>> groupBy(Function classifier, Function valueMapper) {
        return groupBy(classifier, (Collector>) (Collector) Collectors.mapping(valueMapper, Collectors.toList()));
    }

    @Override
    @SuppressWarnings("rawtypes")
    public  Stream>> groupBy(Function classifier, Function valueMapper,
            Supplier>> mapFactory) {
        final Map> map = collect(
                Collectors.groupingBy(classifier, (Collector>) (Collector) Collectors.mapping(valueMapper, Collectors.toList()), mapFactory));

        return newStream(map.entrySet().iterator(), false, null);
    }

    @Override
    public  Stream> groupBy(final Function classifier, Collector downstream) {
        final Map map = collect(Collectors.groupingBy(classifier, downstream));

        return newStream(map.entrySet().iterator(), false, null);
    }

    @Override
    public  Stream> groupBy(final Function classifier, Collector downstream,
            Supplier> mapFactory) {
        final Map map = collect(Collectors.groupingBy(classifier, downstream, mapFactory));

        return newStream(map.entrySet().iterator(), false, null);
    }

    @Override
    public  Stream> groupBy(final Function classifier, final Function valueMapper,
            BinaryOperator mergeFunction) {
        final Map map = collect(Collectors.toMap(classifier, valueMapper, mergeFunction));

        return newStream(map.entrySet().iterator(), false, null);
    }

    @Override
    public  Stream> groupBy(final Function classifier, final Function valueMapper,
            BinaryOperator mergeFunction, Supplier> mapFactory) {
        final Map map = collect(Collectors.toMap(classifier, valueMapper, mergeFunction, mapFactory));

        return newStream(map.entrySet().iterator(), false, null);
    }

    @Override
    public  EntryStream> groupByToEntry(Function classifier) {
        final Function>, Map.Entry>> mapper = Fn.identity();
        final Function classifier2 = (Function) classifier;

        return groupBy(classifier2).mapToEntry(mapper);
    }

    @Override
    public  EntryStream> groupByToEntry(Function classifier, Supplier>> mapFactory) {
        final Function>, Map.Entry>> mapper = Fn.identity();

        return groupBy(classifier, mapFactory).mapToEntry(mapper);
    }

    @Override
    public  EntryStream> groupByToEntry(Function classifier, Function valueMapper) {
        final Function>, Map.Entry>> mapper = Fn.identity();
        final Function classifier2 = (Function) classifier;
        final Function valueMapper2 = (Function) valueMapper;

        return groupBy(classifier2, valueMapper2).mapToEntry(mapper);
    }

    @Override
    public  EntryStream> groupByToEntry(Function classifier, Function valueMapper,
            Supplier>> mapFactory) {
        final Function>, Map.Entry>> mapper = Fn.identity();

        return groupBy(classifier, valueMapper, mapFactory).mapToEntry(mapper);
    }

    @Override
    public  EntryStream groupByToEntry(Function classifier, Collector downstream) {
        final Function, Map.Entry> mapper = Fn.identity();
        final Function classifier2 = (Function) classifier;

        return groupBy(classifier2, downstream).mapToEntry(mapper);
    }

    @Override
    public  EntryStream groupByToEntry(Function classifier, Collector downstream,
            Supplier> mapFactory) {
        final Function, Map.Entry> mapper = Fn.identity();

        return groupBy(classifier, downstream, mapFactory).mapToEntry(mapper);
    }

    @Override
    public  EntryStream groupByToEntry(Function classifier, Function valueMapper,
            BinaryOperator mergeFunction) {
        final Function, Map.Entry> mapper = Fn.identity();
        final Function classifier2 = (Function) classifier;
        final Function valueMapper2 = (Function) valueMapper;

        return groupBy(classifier2, valueMapper2, mergeFunction).mapToEntry(mapper);
    }

    @Override
    public  EntryStream groupByToEntry(Function classifier, Function valueMapper,
            BinaryOperator mergeFunction, Supplier> mapFactory) {
        final Function, Map.Entry> mapper = Fn.identity();

        return groupBy(classifier, valueMapper, mergeFunction, mapFactory).mapToEntry(mapper);
    }

    @Override
    public Stream>> partitionBy(Predicate predicate) {
        final Map> map = collect(Collectors.partitioningBy(predicate));

        return newStream(map.entrySet().iterator(), false, null);
    }

    @Override
    public  Stream> partitionBy(Predicate predicate, Collector downstream) {
        final Map map = collect(Collectors.partitioningBy(predicate, downstream));

        return newStream(map.entrySet().iterator(), false, null);
    }

    @Override
    public EntryStream> partitionByToEntry(Predicate predicate) {
        final Function>, Map.Entry>> mapper = Fn.identity();

        return partitionBy(predicate).mapToEntry(mapper);
    }

    @Override
    public  EntryStream partitionByToEntry(Predicate predicate, Collector downstream) {
        final Function, Map.Entry> mapper = Fn.identity();

        return partitionBy(predicate, downstream).mapToEntry(mapper);
    }

    @Override
    public  Map toMap(Function keyExtractor, Function valueMapper) {
        final Supplier> mapFactory = Fn.Suppliers.ofMap();

        return toMap(keyExtractor, valueMapper, mapFactory);
    }

    @Override
    public > M toMap(Function keyExtractor, Function valueMapper,
            Supplier mapFactory) {
        final BinaryOperator mergeFunction = Fn.throwingMerger();

        return toMap(keyExtractor, valueMapper, mergeFunction, mapFactory);
    }

    @Override
    public  Map toMap(Function keyExtractor, Function valueMapper,
            BinaryOperator mergeFunction) {
        final Supplier> mapFactory = Fn.Suppliers.ofMap();

        return toMap(keyExtractor, valueMapper, mergeFunction, mapFactory);
    }

    @Override
    public  Map toMap(Function classifier, Collector downstream) {
        final Supplier> mapFactory = Fn.Suppliers.ofMap();

        return toMap(classifier, downstream, mapFactory);
    }

    @Override
    public  Map> groupTo(Function classifier) {
        final Supplier>> mapFactory = Fn.Suppliers.ofMap();

        return groupTo(classifier, mapFactory);
    }

    @Override
    public >> M groupTo(Function classifier, Supplier mapFactory) {
        final Collector> downstream = Collectors.toList();

        return toMap(classifier, downstream, mapFactory);
    }

    @Override
    public  Map> groupTo(Function keyExtractor, Function valueMapper) {
        return toMap(keyExtractor, (Collector>) (Collector) Collectors.mapping(valueMapper, Collectors.toList()));
    }

    @SuppressWarnings("rawtypes")
    @Override
    public >> M groupTo(Function keyExtractor, Function valueMapper,
            Supplier mapFactory) {
        return toMap(keyExtractor, (Collector>) (Collector) Collectors.mapping(valueMapper, Collectors.toList()), mapFactory);
    }

    @Override
    public  ListMultimap toMultimap(Function keyExtractor) {
        return toMultimap(keyExtractor, new Function() {
            @Override
            public T apply(T t) {
                return t;
            }
        });
    }

    @Override
    public , M extends Multimap> M toMultimap(Function keyExtractor, Supplier mapFactory) {
        return toMultimap(keyExtractor, new Function() {
            @Override
            public T apply(T t) {
                return t;
            }
        }, mapFactory);
    }

    @Override
    public  ListMultimap toMultimap(Function keyExtractor, Function valueMapper) {
        return toMultimap(keyExtractor, valueMapper, new Supplier>() {
            @Override
            public ListMultimap get() {
                return N.newListMultimap();
            }
        });
    }

    @Override
    public Matrix toMatrix(final Class type) {
        final T[] a = toArray(new IntFunction() {
            @Override
            public T[] apply(int value) {
                return (T[]) N.newArray(type, 0);
            }
        });

        return Matrix.of(a);
    }

    @Override
    public int sumInt(ToIntFunction mapper) {
        return collect(Collectors.summingInt(mapper));
    }

    @Override
    public long sumLong(ToLongFunction mapper) {
        return collect(Collectors.summingLong(mapper));
    }

    @Override
    public double sumDouble(ToDoubleFunction mapper) {
        return collect(Collectors.summingDouble(mapper));
    }

    @Override
    public OptionalDouble averageInt(ToIntFunction mapper) {
        return collect(Collectors.averagingInt2(mapper));
    }

    @Override
    public OptionalDouble averageLong(ToLongFunction mapper) {
        return collect(Collectors.averagingLong2(mapper));
    }

    @Override
    public OptionalDouble averageDouble(ToDoubleFunction mapper) {
        return collect(Collectors.averagingDouble2(mapper));
    }

    @Override
    public  Stream> innerJoin(final Collection b, final Function leftKeyMapper, final Function rightKeyMapper) {
        final ListMultimap rightKeyMap = ListMultimap.from(b, rightKeyMapper);
        final Map> rightKeyStreamMap = new HashMap<>(N.initHashCapacity(rightKeyMap.size()));

        for (Map.Entry> entry : rightKeyMap.entrySet()) {
            rightKeyStreamMap.put(entry.getKey(), Stream.of(entry.getValue()).cached());
        }

        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                final Stream s = rightKeyStreamMap.get(leftKeyMapper.apply(t));

                return s == null ? Stream.> empty() : s.map(new Function>() {
                    @Override
                    public Pair apply(U u) {
                        return Pair.of(t, u);
                    }
                });
            }
        });
    }

    @Override
    public  Stream> innerJoin(final Collection b, final BiPredicate predicate) {
        final Stream s = Stream.of(b).cached();

        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                return s.filter(new Predicate() {
                    @Override
                    public boolean test(final U u) {
                        return predicate.test(t, u);
                    }
                }).map(new Function>() {
                    @Override
                    public Pair apply(U u) {
                        return Pair.of(t, u);
                    }
                });
            }
        });
    }

    @Override
    public  Stream> fullJoin(final Collection b, final Function leftKeyMapper, final Function rightKeyMapper) {
        final ListMultimap rightKeyMap = ListMultimap.from(b, rightKeyMapper);
        final Map> rightKeyStreamMap = new HashMap<>(N.initHashCapacity(rightKeyMap.size()));
        final Map joinedRights = new IdentityHashMap<>();
        final boolean isParallelStream = this.isParallel();

        for (Map.Entry> entry : rightKeyMap.entrySet()) {
            rightKeyStreamMap.put(entry.getKey(), Stream.of(entry.getValue()).cached());
        }

        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                final Stream s = rightKeyStreamMap.get(leftKeyMapper.apply(t));

                return s == null ? Stream.of(Pair.of(t, (U) null)) : s.map(new Function>() {
                    @Override
                    public Pair apply(U u) {
                        if (isParallelStream) {
                            synchronized (joinedRights) {
                                joinedRights.put(u, u);
                            }
                        } else {
                            joinedRights.put(u, u);
                        }

                        return Pair.of(t, u);
                    }
                });
            }
        }).append(Stream.of(b).filter(new Predicate() {
            @Override
            public boolean test(U u) {
                return joinedRights.containsKey(u) == false;
            }
        }).map(new Function>() {
            @Override
            public Pair apply(U u) {
                return Pair.of((T) null, u);
            }
        }));
    }

    @Override
    public  Stream> fullJoin(final Collection b, final BiPredicate predicate) {
        final Stream s = Stream.of(b).cached();
        final Map joinedRights = new IdentityHashMap<>();
        final boolean isParallelStream = this.isParallel();

        return flatMap(new Function>>() {
            private final MutableBoolean joined = MutableBoolean.of(false);

            @Override
            public Stream> apply(final T t) {
                return s.filter(new Predicate() {
                    @Override
                    public boolean test(final U u) {
                        return predicate.test(t, u);
                    }
                }).map(new Function>() {
                    @Override
                    public Pair apply(U u) {
                        joined.setTrue();

                        if (isParallelStream) {
                            synchronized (joinedRights) {
                                joinedRights.put(u, u);
                            }
                        } else {
                            joinedRights.put(u, u);
                        }

                        return Pair.of(t, u);
                    }
                }).append(Stream.iterate(new Supplier() {
                    @Override
                    public Boolean get() {
                        return joined.isFalse();
                    }
                }, new Supplier>() {
                    @Override
                    public Pair get() {
                        joined.setTrue();
                        return Pair.of(t, (U) null);
                    }
                }));
            }
        }).append(Stream.of(b).filter(new Predicate() {
            @Override
            public boolean test(U u) {
                return joinedRights.containsKey(u) == false;
            }
        }).map(new Function>() {
            @Override
            public Pair apply(U u) {
                return Pair.of((T) null, u);
            }
        }));
    }

    @Override
    public  Stream> leftJoin(final Collection b, final Function leftKeyMapper, final Function rightKeyMapper) {
        final ListMultimap rightKeyMap = ListMultimap.from(b, rightKeyMapper);
        final Map> rightKeyStreamMap = new HashMap<>(N.initHashCapacity(rightKeyMap.size()));

        for (Map.Entry> entry : rightKeyMap.entrySet()) {
            rightKeyStreamMap.put(entry.getKey(), Stream.of(entry.getValue()).cached());
        }

        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                final Stream s = rightKeyStreamMap.get(leftKeyMapper.apply(t));

                return s == null ? Stream.of(Pair.of(t, (U) null)) : s.map(new Function>() {
                    @Override
                    public Pair apply(U u) {
                        return Pair.of(t, u);
                    }
                });
            }
        });
    }

    @Override
    public  Stream> leftJoin(final Collection b, final BiPredicate predicate) {
        final Stream s = Stream.of(b).cached();

        return flatMap(new Function>>() {
            private final MutableBoolean joined = MutableBoolean.of(false);

            @Override
            public Stream> apply(final T t) {
                return s.filter(new Predicate() {
                    @Override
                    public boolean test(final U u) {
                        return predicate.test(t, u);
                    }
                }).map(new Function>() {
                    @Override
                    public Pair apply(U u) {
                        joined.setTrue();

                        return Pair.of(t, u);
                    }
                }).append(Stream.iterate(new Supplier() {
                    @Override
                    public Boolean get() {
                        return joined.isFalse();
                    }
                }, new Supplier>() {
                    @Override
                    public Pair get() {
                        joined.setTrue();
                        return Pair.of(t, (U) null);
                    }
                }));
            }
        });
    }

    @Override
    public  Stream> rightJoin(final Collection b, final Function leftKeyMapper, final Function rightKeyMapper) {
        final ListMultimap rightKeyMap = ListMultimap.from(b, rightKeyMapper);
        final Map> rightKeyStreamMap = new HashMap<>(N.initHashCapacity(rightKeyMap.size()));
        final Map joinedRights = new IdentityHashMap<>();
        final boolean isParallelStream = this.isParallel();

        for (Map.Entry> entry : rightKeyMap.entrySet()) {
            rightKeyStreamMap.put(entry.getKey(), Stream.of(entry.getValue()).cached());
        }

        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                final Stream s = rightKeyStreamMap.get(leftKeyMapper.apply(t));

                return s == null ? Stream.> empty() : s.map(new Function>() {
                    @Override
                    public Pair apply(U u) {
                        if (isParallelStream) {
                            synchronized (joinedRights) {
                                joinedRights.put(u, u);
                            }
                        } else {
                            joinedRights.put(u, u);
                        }

                        return Pair.of(t, u);
                    }
                });
            }
        }).append(Stream.of(b).filter(new Predicate() {
            @Override
            public boolean test(U u) {
                return joinedRights.containsKey(u) == false;
            }
        }).map(new Function>() {
            @Override
            public Pair apply(U u) {
                return Pair.of((T) null, u);
            }
        }));
    }

    @Override
    public  Stream> rightJoin(final Collection b, final BiPredicate predicate) {
        final Stream s = Stream.of(b).cached();
        final Map joinedRights = new IdentityHashMap<>();
        final boolean isParallelStream = this.isParallel();

        return flatMap(new Function>>() {

            @Override
            public Stream> apply(final T t) {
                return s.filter(new Predicate() {
                    @Override
                    public boolean test(final U u) {
                        return predicate.test(t, u);
                    }
                }).map(new Function>() {
                    @Override
                    public Pair apply(U u) {

                        if (isParallelStream) {
                            synchronized (joinedRights) {
                                joinedRights.put(u, u);
                            }
                        } else {
                            joinedRights.put(u, u);
                        }

                        return Pair.of(t, u);
                    }
                });
            }
        }).append(Stream.of(b).filter(new Predicate() {
            @Override
            public boolean test(U u) {
                return joinedRights.containsKey(u) == false;
            }
        }).map(new Function>() {
            @Override
            public Pair apply(U u) {
                return Pair.of((T) null, u);
            }
        }));
    }

    @Override
    public CharSummaryStatistics summarizeChar(ToCharFunction mapper) {
        return collect(Collectors.summarizingChar(mapper));
    }

    @Override
    public ByteSummaryStatistics summarizeByte(ToByteFunction mapper) {
        return collect(Collectors.summarizingByte(mapper));
    }

    @Override
    public ShortSummaryStatistics summarizeShort(ToShortFunction mapper) {
        return collect(Collectors.summarizingShort(mapper));
    }

    @Override
    public IntSummaryStatistics summarizeInt(ToIntFunction mapper) {
        return collect(Collectors.summarizingInt(mapper));
    }

    @Override
    public LongSummaryStatistics summarizeLong(ToLongFunction mapper) {
        return collect(Collectors.summarizingLong(mapper));
    }

    @Override
    public FloatSummaryStatistics summarizeFloat(ToFloatFunction mapper) {
        return collect(Collectors.summarizingFloat(mapper));
    }

    @Override
    public DoubleSummaryStatistics summarizeDouble(ToDoubleFunction mapper) {
        return collect(Collectors.summarizingDouble(mapper));
    }

    @Override
    public  NullabLe findFirst(final U seed, final BiPredicate predicate) {
        return findFirst(new Predicate() {
            @Override
            public boolean test(T t) {
                return predicate.test(t, seed);
            }
        });
    }

    @Override
    public  NullabLe findLast(final U seed, final BiPredicate predicate) {
        return findLast(new Predicate() {
            @Override
            public boolean test(T t) {
                return predicate.test(t, seed);
            }
        });
    }

    @Override
    public NullabLe findFirstOrLast(final Predicate predicateForFirst, final Predicate predicateForLast) {
        final ExIterator iter = exIterator();
        T last = (T) NONE;
        T next = null;

        while (iter.hasNext()) {
            next = iter.next();

            if (predicateForFirst.test(next)) {
                return NullabLe.of(next);
            } else if (predicateForLast.test(next)) {
                last = next;
            }
        }

        return last == NONE ? (NullabLe) NullabLe.empty() : NullabLe.of(last);
    }

    @Override
    public  NullabLe findFirstOrLast(final U seed, final BiPredicate predicateForFirst,
            final BiPredicate predicateForLast) {
        final ExIterator iter = exIterator();
        T last = (T) NONE;
        T next = null;

        while (iter.hasNext()) {
            next = iter.next();

            if (predicateForFirst.test(next, seed)) {
                return NullabLe.of(next);
            } else if (predicateForLast.test(next, seed)) {
                last = next;
            }
        }

        return last == NONE ? (NullabLe) NullabLe.empty() : NullabLe.of(last);
    }

    @Override
    public  NullabLe findFirstOrLast(final Function preFunc, final BiPredicate predicateForFirst,
            final BiPredicate predicateForLast) {
        final ExIterator iter = exIterator();
        U seed = null;
        T last = (T) NONE;
        T next = null;

        while (iter.hasNext()) {
            next = iter.next();
            seed = preFunc.apply(next);

            if (predicateForFirst.test(next, seed)) {
                return NullabLe.of(next);
            } else if (predicateForLast.test(next, seed)) {
                last = next;
            }
        }

        return last == NONE ? (NullabLe) NullabLe.empty() : NullabLe.of(last);
    }

    @Override
    public  NullabLe findAny(final U seed, final BiPredicate predicate) {
        return findAny(new Predicate() {
            @Override
            public boolean test(T t) {
                return predicate.test(t, seed);
            }
        });
    }

    @Override
    public  boolean anyMatch(final U seed, final BiPredicate predicate) {
        return anyMatch(new Predicate() {
            @Override
            public boolean test(T t) {
                return predicate.test(t, seed);
            }
        });
    }

    @Override
    public  boolean allMatch(final U seed, final BiPredicate predicate) {
        return allMatch(new Predicate() {
            @Override
            public boolean test(T t) {
                return predicate.test(t, seed);
            }
        });
    }

    @Override
    public  boolean noneMatch(final U seed, final BiPredicate predicate) {
        return noneMatch(new Predicate() {
            @Override
            public boolean test(T t) {
                return predicate.test(t, seed);
            }
        });
    }

    @Override
    public boolean containsAll(final T... a) {
        if (N.isNullOrEmpty(a)) {
            return true;
        } else if (a.length == 1) {
            return anyMatch(Fn.equal(a[0]));
        } else if (a.length == 2) {
            return filter(new Predicate() {
                private final T val1 = a[0];
                private final T val2 = a[1];

                @Override
                public boolean test(T t) {
                    return N.equals(t, val1) || N.equals(t, val2);
                }
            }).distinct().limit(2).count() == 2;
        } else {
            return containsAll(N.asSet(a));
        }
    }

    @Override
    public boolean containsAll(final Collection c) {
        if (N.isNullOrEmpty(c)) {
            return true;
        } else if (c.size() == 1) {
            final T val = c instanceof List ? ((List) c).get(0) : c.iterator().next();
            return anyMatch(Fn.equal(val));
        } else {
            final Set set = c instanceof Set ? (Set) c : N.newHashSet(c);
            return filter(new Predicate() {
                @Override
                public boolean test(T t) {
                    return set.contains(t);
                }
            }).distinct().limit(set.size()).count() == set.size();
        }
    }

    @Override
    public NullabLe first() {
        final Iterator iter = this.iterator();

        if (iter.hasNext() == false) {
            return NullabLe.empty();
        }

        return NullabLe.of(iter.next());
    }

    @Override
    public NullabLe last() {
        final Iterator iter = this.iterator();

        if (iter.hasNext() == false) {
            return NullabLe.empty();
        }

        T next = iter.next();

        while (iter.hasNext()) {
            next = iter.next();
        }

        return NullabLe.of(next);
    }

    @Override
    public Stream skipNull() {
        return filter(Fn.notNull());
    }

    @Override
    public Stream> splitAt(final int n) {
        if (n < 0) {
            throw new IllegalArgumentException("'n' can't be negative");
        }

        final Iterator iter = this.iterator();
        final List list = new ArrayList<>();

        while (list.size() < n && iter.hasNext()) {
            list.add(iter.next());
        }

        final Stream[] a = new Stream[] { new ArrayStream<>(toArray(list), 0, list.size(), null, sorted, cmp),
                new IteratorStream<>(iter, null, sorted, cmp) };

        return newStream(a, false, null);
    }

    @Override
    public Stream> splitBy(Predicate where) {
        N.requireNonNull(where);

        final Iterator iter = this.iterator();
        final List list = new ArrayList<>();
        T next = null;
        Stream s = null;

        while (iter.hasNext()) {
            next = iter.next();

            if (where.test(next)) {
                list.add(next);
            } else {
                s = Stream.of(next);

                break;
            }
        }

        final Stream[] a = new Stream[] { new ArrayStream<>((T[]) list.toArray(), null, sorted, cmp), new IteratorStream<>(iter, null, sorted, cmp) };

        if (s != null) {
            if (sorted) {
                a[1] = new IteratorStream<>(a[1].prepend(s).iterator(), null, sorted, cmp);
            } else {
                a[1] = a[1].prepend(s);
            }
        }

        return this.newStream(a, false, null);
    }

    @Override
    public Stream> slidingToList(int windowSize) {
        return slidingToList(windowSize, 1);
    }

    @Override
    public Stream intersection(final Collection c) {
        final Multiset multiset = Multiset.from(c);

        return newStream(this.sequential().filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return multiset.getAndRemove(value) > 0;
            }
        }).iterator(), sorted, cmp);
    }

    @Override
    public Stream intersection(final Function mapper, final Collection c) {
        final Multiset multiset = Multiset.from(c);

        return newStream(this.sequential().filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return multiset.getAndRemove(mapper.apply(value)) > 0;
            }
        }).iterator(), sorted, cmp);
    }

    @Override
    public Stream difference(final Collection c) {
        final Multiset multiset = Multiset.from(c);

        return newStream(this.sequential().filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return multiset.getAndRemove(value) < 1;
            }
        }).iterator(), sorted, cmp);
    }

    @Override
    public Stream difference(final Function mapper, final Collection c) {
        final Multiset multiset = Multiset.from(c);

        return newStream(this.sequential().filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return multiset.getAndRemove(mapper.apply(value)) < 1;
            }
        }).iterator(), sorted, cmp);
    }

    @Override
    public Stream symmetricDifference(final Collection c) {
        final Multiset multiset = Multiset.from(c);

        return newStream(this.sequential().filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return multiset.getAndRemove(value) < 1;
            }
        }).append(Stream.of(c).filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return multiset.getAndRemove(value) > 0;
            }
        })).iterator(), false, null);
    }

    @Override
    public Stream reversed() {
        final T[] tmp = (T[]) toArray();

        return newStream(new ExIterator() {
            private int cursor = tmp.length;

            @Override
            public boolean hasNext() {
                return cursor > 0;
            }

            @Override
            public T next() {
                if (cursor <= 0) {
                    throw new NoSuchElementException();
                }

                return tmp[--cursor];
            }

            @Override
            public long count() {
                return cursor;
            }

            @Override
            public void skip(long n) {
                cursor = n < cursor ? cursor - (int) n : 0;
            }

            @Override
            public  A[] toArray(A[] a) {
                a = a.length >= cursor ? a : (A[]) N.newArray(a.getClass().getComponentType(), cursor);

                for (int i = 0, len = a.length; i < len; i++) {
                    a[i] = (A) tmp[cursor - i - 1];
                }

                return a;
            }
        }, false, null);
    }

    @Override
    public Stream shuffled() {
        final T[] a = (T[]) toArray();

        N.shuffle(a);

        return newStream(a, false, null);
    }

    @Override
    public Stream shuffled(final Random rnd) {
        final T[] a = (T[]) toArray();

        N.shuffle(a, rnd);

        return newStream(a, false, null);
    }

    @Override
    public Stream rotated(int distance) {
        final T[] a = (T[]) toArray();

        N.rotate(a, distance);

        return newStream(a, false, null);
    }

    @Override
    public Stream reverseSorted() {
        return sorted(Fn.reversedOrder());
    }

    @Override
    public Stream distinct() {
        final Set set = new HashSet<>();

        return newStream(this.sequential().filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return set.add(hashKey(value));
            }
        }).iterator(), sorted, cmp);
    }

    @Override
    public Stream distinctBy(final Function keyExtractor) {
        final Set set = new HashSet<>();

        return newStream(this.sequential().filter(new Predicate() {
            @Override
            public boolean test(T value) {
                return set.add(hashKey(keyExtractor.apply(value)));
            }
        }).iterator(), sorted, cmp);
    }

    @Override
    public Optional> distribution() {
        final Object[] a = sorted().toArray();

        if (N.isNullOrEmpty(a)) {
            return Optional.empty();
        }

        return Optional.of((Map) N.distribution(a));
    }

    @Override
    public Optional> distribution(Comparator comparator) {
        final Object[] a = sorted(comparator).toArray();

        if (N.isNullOrEmpty(a)) {
            return Optional.empty();
        }

        return Optional.of((Map) N.distribution(a));
    }

    @Override
    public Pair>> summarizeChar2(ToCharFunction mapper) {
        final char[] a = mapToChar(mapper).sorted().toArray();

        if (N.isNullOrEmpty(a)) {
            return Pair.of(new CharSummaryStatistics(), Optional.> empty());
        } else {
            return Pair.of(new CharSummaryStatistics(a.length, sum(a), a[0], a[a.length - 1]), Optional.of(N.distribution(a)));
        }
    }

    @Override
    public Pair>> summarizeByte2(ToByteFunction mapper) {
        final byte[] a = mapToByte(mapper).sorted().toArray();

        if (N.isNullOrEmpty(a)) {
            return Pair.of(new ByteSummaryStatistics(), Optional.> empty());
        } else {
            return Pair.of(new ByteSummaryStatistics(a.length, sum(a), a[0], a[a.length - 1]), Optional.of(N.distribution(a)));
        }
    }

    @Override
    public Pair>> summarizeShort2(ToShortFunction mapper) {
        final short[] a = mapToShort(mapper).sorted().toArray();

        if (N.isNullOrEmpty(a)) {
            return Pair.of(new ShortSummaryStatistics(), Optional.> empty());
        } else {
            return Pair.of(new ShortSummaryStatistics(a.length, sum(a), a[0], a[a.length - 1]), Optional.of(N.distribution(a)));
        }
    }

    @Override
    public Pair>> summarizeInt2(ToIntFunction mapper) {
        final int[] a = mapToInt(mapper).sorted().toArray();

        if (N.isNullOrEmpty(a)) {
            return Pair.of(new IntSummaryStatistics(), Optional.> empty());
        } else {
            return Pair.of(new IntSummaryStatistics(a.length, sum(a), a[0], a[a.length - 1]), Optional.of(N.distribution(a)));
        }
    }

    @Override
    public Pair>> summarizeLong2(ToLongFunction mapper) {
        final long[] a = mapToLong(mapper).sorted().toArray();

        if (N.isNullOrEmpty(a)) {
            return Pair.of(new LongSummaryStatistics(), Optional.> empty());
        } else {
            return Pair.of(new LongSummaryStatistics(a.length, sum(a), a[0], a[a.length - 1]), Optional.of(N.distribution(a)));
        }
    }

    @Override
    public Pair>> summarizeFloat2(ToFloatFunction mapper) {
        final float[] a = mapToFloat(mapper).sorted().toArray();

        if (N.isNullOrEmpty(a)) {
            return Pair.of(new FloatSummaryStatistics(), Optional.> empty());
        } else {
            return Pair.of(new FloatSummaryStatistics(a.length, sum(a), a[0], a[a.length - 1]), Optional.of(N.distribution(a)));
        }
    }

    @Override
    public Pair>> summarizeDouble2(ToDoubleFunction mapper) {
        final double[] a = mapToDouble(mapper).sorted().toArray();

        if (N.isNullOrEmpty(a)) {
            return Pair.of(new DoubleSummaryStatistics(), Optional.> empty());
        } else {
            return Pair.of(new DoubleSummaryStatistics(a.length, sum(a), a[0], a[a.length - 1]), Optional.of(N.distribution(a)));
        }
    }

    //    @Override
    //    public Stream> powerSet() {
    //        final Set set = toSet(new Supplier>() {
    //            @Override
    //            public Set get() {
    //                return new LinkedHashSet<>();
    //            }
    //        });
    //
    //        return newStream(Seq.powerSet(set).iterator(), false, null);
    //    }

    @Override
    public Stream> combinations() {
        if (this instanceof ArrayStream) {
            return newStream(IntStream.rangeClosed(0, (int) count()).flatMapToObj(new IntFunction>>() {
                @Override
                public Stream> apply(int value) {
                    return combinations(value);
                }
            }).iterator(), false, null);
        } else {
            return newStream((T[]) toArray(), false, null).combinations();
        }
    }

    @Override
    public Stream> combinations(final int len) {
        if (this instanceof ArrayStream) {
            N.checkFromIndexSize(0, len, (int) count());

            if (len == 0) {
                return newStream(N.asArray((List) N.EMPTY_LIST), false, null);
            } else if (len == 1) {
                return map(new Function>() {
                    @Override
                    public List apply(T t) {
                        return N.asList(t);
                    }
                });
            } else if (len == count()) {
                return newStream(N.asArray(toList()), false, null);
            } else {
                final T[] a = ((ArrayStream) this).elements;
                final int fromIndex = ((ArrayStream) this).fromIndex;
                final int toIndex = ((ArrayStream) this).toIndex;

                return newStream(new ExIterator>() {

                    private final int[] indices = Array.range(fromIndex, fromIndex + len);

                    @Override
                    public boolean hasNext() {
                        return indices[0] <= toIndex - len;
                    }

                    @Override
                    public List next() {
                        final List result = new ArrayList<>(len);

                        for (int idx : indices) {
                            result.add(a[idx]);
                        }

                        if (++indices[len - 1] == toIndex) {
                            for (int i = len - 1; i > 0; i--) {
                                if (indices[i] > toIndex - (len - i)) {
                                    indices[i - 1]++;

                                    for (int j = i; j < len; j++) {
                                        indices[j] = indices[j - 1] + 1;
                                    }
                                }
                            }
                        }

                        return result;
                    }

                }, false, null);
            }
        } else {
            return newStream((T[]) toArray(), false, null).combinations(len);
        }
    }

    @Override
    public Stream> permutations() {
        return newStream(PermutationIterator.of(toList()), false, null);
    }

    @Override
    public Stream> orderedPermutations() {
        return orderedPermutations(OBJECT_COMPARATOR);
    }

    @Override
    public Stream> orderedPermutations(Comparator comparator) {
        final Iterator> iter = PermutationIterator.ordered(toList(), comparator == null ? OBJECT_COMPARATOR : comparator);

        return newStream(iter, false, null);
    }

    @Override
    public Stream> cartesianProduct(final Collection> cs) {
        final List> cList = new ArrayList<>(cs.size() + 1);
        cList.add(this.toList());
        cList.addAll(cs);

        return newStream(Seq.cartesianProduct(cList).iterator(), false, null);
    }

    @Override
    public DataSet toDataSet() {
        return toDataSet(null);
    }

    @Override
    public DataSet toDataSet(List columnNames) {
        return N.newDataSet(columnNames, toList());
    }

    @Override
    public String join(CharSequence delimiter) {
        final Function mapper = new Function() {
            @SuppressWarnings("rawtypes")
            @Override
            public String apply(T t) {
                return t instanceof BaseStream ? ((BaseStream) t).join(", ", "[", "]") : N.toString(t);
            }
        };

        return this.map(mapper).collect(Collectors.joining(delimiter));
    }

    @Override
    public String join(CharSequence delimiter, CharSequence prefix, CharSequence suffix) {
        final Function mapper = new Function() {
            @SuppressWarnings("rawtypes")
            @Override
            public String apply(T t) {
                return t instanceof BaseStream ? ((BaseStream) t).join(", ", "[", "]") : N.toString(t);
            }
        };

        return this.map(mapper).collect(Collectors.joining(delimiter, prefix, suffix));
    }

    @Override
    public boolean hasDuplicates() {
        final Set set = new HashSet<>();
        final Iterator iter = iterator();

        while (iter.hasNext()) {
            if (set.add(iter.next()) == false) {
                return true;
            }
        }

        return false;
    }

    @Override
    public  U reduce(U identity, BiFunction accumulator) {
        final BinaryOperator combiner = reducingCombiner;

        return reduce(identity, accumulator, combiner);
    }

    @Override
    public  R collect(Supplier supplier, BiConsumer accumulator) {
        final BiConsumer combiner = collectingCombiner;

        return collect(supplier, accumulator, combiner);
    }

    @Override
    public  R collect(java.util.stream.Collector collector) {
        return collect(Collector.of(collector));
    }

    @Override
    public  RR collectAndThen(Collector downstream, Function finisher) {
        return finisher.apply(collect(downstream));
    }

    @Override
    public  RR collectAndThen(java.util.stream.Collector downstream, Function finisher) {
        return finisher.apply(collect(downstream));
    }

    @Override
    public  R toListAndThen(final Function, R> func) {
        return func.apply(toList());
    }

    @Override
    public  R toSetAndThen(final Function, R> func) {
        return func.apply(toSet());
    }

    @Override
    public Pair, Stream> headAndTail() {
        return Pair.of(head(), tail());
    }

    @Override
    public Pair, NullabLe> headAndTail2() {
        return Pair.of(head2(), tail2());
    }

    @Override
    public Stream> indexed() {
        final MutableLong idx = MutableLong.of(0);

        return newStream(this.sequential().map(new Function>() {
            @Override
            public Indexed apply(T t) {
                return Indexed.of(t, idx.getAndIncrement());
            }
        }).iterator(), true, INDEXED_COMPARATOR);
    }

    @Override
    public Stream queued() {
        return queued(DEFAULT_QUEUE_SIZE_PER_ITERATOR);
    }

    @Override
    public Stream append(Stream stream) {
        return Stream.concat(this, stream);
    }

    @Override
    public Stream append(Collection c) {
        return append(Stream.of(c));
    }

    @Override
    public Stream prepend(Stream stream) {
        return Stream.concat(stream, this);
    }

    @Override
    public Stream prepend(Collection c) {
        return prepend(Stream.of(c));
    }

    @Override
    public Stream merge(final Stream b, final BiFunction nextSelector) {
        return Stream.merge(this, b, nextSelector);
    }

    @Override
    public  Stream zipWith(Stream b, BiFunction zipFunction) {
        return Stream.zip(this, b, zipFunction);
    }

    @Override
    public  Stream zipWith(Stream b, Stream c, TriFunction zipFunction) {
        return Stream.zip(this, b, c, zipFunction);
    }

    @Override
    public  Stream zipWith(Stream b, T valueForNoneA, T2 valueForNoneB, BiFunction zipFunction) {
        return Stream.zip(this, b, valueForNoneA, valueForNoneB, zipFunction);
    }

    @Override
    public  Stream zipWith(Stream b, Stream c, T valueForNoneA, T2 valueForNoneB, T3 valueForNoneC,
            TriFunction zipFunction) {
        return Stream.zip(this, b, c, valueForNoneA, valueForNoneB, valueForNoneC, zipFunction);
    }

    @Override
    public Stream cached() {
        return newStream((T[]) toArray(), sorted, cmp);
    }

    @Override
    public Stream cached(IntFunction generator) {
        return newStream(toArray(generator), sorted, cmp);
    }

    @Override
    public long persist(File file, Function toLine) {
        Writer writer = null;

        try {
            writer = new FileWriter(file);
            return persist(writer, toLine);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        } finally {
            IOUtil.close(writer);
        }
    }

    @Override
    public long persist(OutputStream os, Function toLine) {
        final BufferedWriter bw = ObjectFactory.createBufferedWriter(os);

        try {
            return persist(bw, toLine);
        } finally {
            ObjectFactory.recycle(bw);
        }
    }

    @Override
    public long persist(Writer writer, Function toLine) {
        final Iterator iter = iterator();
        final BufferedWriter bw = writer instanceof BufferedWriter ? (BufferedWriter) writer : ObjectFactory.createBufferedWriter(writer);
        long cnt = 0;

        try {
            while (iter.hasNext()) {
                bw.write(toLine.apply(iter.next()));
                bw.write(N.LINE_SEPARATOR);
                cnt++;
            }
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        } finally {
            if (bw != writer) {
                ObjectFactory.recycle(bw);
            }
        }
        return cnt;
    }

    @Override
    public long persist(final Connection conn, final String insertSQL, final int batchSize, final int batchInterval,
            final Try.BiConsumer stmtSetter) {
        PreparedStatement stmt = null;

        try {
            stmt = conn.prepareStatement(insertSQL);

            return persist(stmt, batchSize, batchInterval, stmtSetter);
        } catch (SQLException e) {
            throw new UncheckedSQLException(e);
        } finally {
            closeQuietly(stmt);
        }
    }

    @Override
    public long persist(final PreparedStatement stmt, final int batchSize, final int batchInterval,
            final Try.BiConsumer stmtSetter) {
        N.checkArgument(batchSize > 0 && batchInterval >= 0, "'batchSize'=%s must be greater than 0 and 'batchInterval'=%s can't be negative", batchSize,
                batchInterval);

        final Iterator iter = iterator();

        long cnt = 0;
        try {
            while (iter.hasNext()) {
                stmtSetter.accept(stmt, iter.next());

                stmt.addBatch();

                if ((++cnt % batchSize) == 0) {
                    stmt.executeBatch();
                    stmt.clearBatch();

                    if (batchInterval > 0) {
                        N.sleep(batchInterval);
                    }
                }
            }

            if ((cnt % batchSize) > 0) {
                stmt.executeBatch();
                stmt.clearBatch();
            }
        } catch (SQLException e) {
            throw new UncheckedSQLException(e);
        }

        return cnt;
    }

    private static void closeQuietly(final Statement stmt) {
        if (stmt != null) {
            if (stmt instanceof PreparedStatement) {
                try {
                    ((PreparedStatement) stmt).clearParameters();
                } catch (Throwable e) {
                    logger.error("Failed to clear parameters", e);
                }
            }

            try {
                stmt.close();
            } catch (Throwable e) {
                logger.error("Failed to close Statement", e);
            }
        }
    }

    @Override
    public  EntryStream mapToEntryER(final Function keyMapper, final Function valueMapper) {
        N.checkState(isParallel() == false, "mapToEntryER can't be applied to parallel stream");

        final Function> mapper = new Function>() {
            private final EntryStream.ReusableEntry entry = new EntryStream.ReusableEntry<>();

            @Override
            public Entry apply(T t) {
                entry.set(keyMapper.apply(t), valueMapper.apply(t));

                return entry;
            }
        };

        return mapToEntry(mapper);
    }
}