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

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

There is a newer version: 1.10.1
Show newest version
/*
 * Copyright (C) 2016, 2017, 2018, 2019 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.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Deque;
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.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import com.landawn.abacus.DataSet;
import com.landawn.abacus.annotation.ParallelSupported;
import com.landawn.abacus.exception.DuplicatedResultException;
import com.landawn.abacus.type.Type;
import com.landawn.abacus.util.Array;
import com.landawn.abacus.util.BufferedWriter;
import com.landawn.abacus.util.Comparators;
import com.landawn.abacus.util.Duration;
import com.landawn.abacus.util.Fn;
import com.landawn.abacus.util.Fn.BiFunctions;
import com.landawn.abacus.util.Fn.Factory;
import com.landawn.abacus.util.Fn.Suppliers;
import com.landawn.abacus.util.IOUtil;
import com.landawn.abacus.util.Indexed;
import com.landawn.abacus.util.Iterables;
import com.landawn.abacus.util.Iterators;
import com.landawn.abacus.util.JdbcUtil;
import com.landawn.abacus.util.Joiner;
import com.landawn.abacus.util.ListMultimap;
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.NoCachingNoUpdating.DisposableEntry;
import com.landawn.abacus.util.Nth;
import com.landawn.abacus.util.ObjIterator;
import com.landawn.abacus.util.Objectory;
import com.landawn.abacus.util.Pair;
import com.landawn.abacus.util.Percentage;
import com.landawn.abacus.util.PermutationIterator;
import com.landawn.abacus.util.StringUtil.Strings;
import com.landawn.abacus.util.Timed;
import com.landawn.abacus.util.Try;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.u.OptionalDouble;
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.BooleanSupplier;
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.LongSupplier;
import com.landawn.abacus.util.function.Predicate;
import com.landawn.abacus.util.function.Supplier;
import com.landawn.abacus.util.function.ToDoubleFunction;
import com.landawn.abacus.util.function.ToIntFunction;
import com.landawn.abacus.util.function.ToLongFunction;
import com.landawn.abacus.util.function.TriFunction;

/**
 *
 * @param  
 */
abstract class AbstractStream extends Stream {

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

    @Override
    public Stream skip(final long n, final Consumer action) {
        final Predicate filter = isParallel() ? new Predicate() {
            final AtomicLong cnt = new AtomicLong(n);

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

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

        return dropWhile(filter, action);
    }

    @Override
    public Stream peekFirst(final Consumer action) {
        final Function mapperForFirst = new Function() {
            @Override
            public T apply(T t) {
                action.accept(t);
                return t;
            }
        };

        return mapFirst(mapperForFirst);
    }

    @Override
    public Stream peekLast(final Consumer action) {
        final Function mapperForFirst = new Function() {
            @Override
            public T apply(T t) {
                action.accept(t);
                return t;
            }
        };

        return mapLast(mapperForFirst);
    }

    @Override
    public Stream removeIf(final Predicate predicate) {
        checkArgNotNull(predicate);

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

    @Override
    public Stream removeIf(final Predicate predicate, final Consumer action) {
        checkArgNotNull(predicate);
        checkArgNotNull(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 dropWhile(final Predicate predicate, final Consumer action) {
        checkArgNotNull(predicate);
        checkArgNotNull(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 step(final long step) {
        checkArgPositive(step, "step");

        final long skip = step - 1;
        final ObjIteratorEx iter = iteratorEx();

        final Iterator iterator = new ObjIteratorEx() {
            @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 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(BiFunction mapper, int increment) {
        return slidingMap(mapper, increment, false);
    }

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

    @Override
    public  Stream slidingMap(TriFunction mapper, int increment) {
        return slidingMap(mapper, increment, false);
    }

    @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 SimpleImmutableEntry(keyMapper.apply(t), valueMapper.apply(t));
            }
        };

        return mapToEntry(mapper);
    }

    //    private static final Predicate> IS_PRESENT = new Predicate>() {
    //        @Override
    //        public boolean test(Optional t) {
    //            return t.isPresent();
    //        }
    //    };
    //
    //    private static final Function, Object> OPTIONAL_GET = new Function, Object>() {
    //        @Override
    //        public Object apply(Optional t) {
    //            return t.get();
    //        }
    //    };
    //
    //    @SuppressWarnings("rawtypes")
    //    @Override
    //    public  Stream mapp(Function> mapper) {
    //        final Function, U> optionalGetter = (Function) OPTIONAL_GET;
    //        return map(mapper).filter(IS_PRESENT).map(optionalGetter);
    //    }

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

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

    @Override
    public CharStream flattMapToChar(final Function mapper) {
        return flatMapToChar(new Function() {
            @Override
            public CharStream apply(T t) {
                return CharStream.of(mapper.apply(t));
            }
        });
    }

    @Override
    public ByteStream flattMapToByte(final Function mapper) {
        return flatMapToByte(new Function() {
            @Override
            public ByteStream apply(T t) {
                return ByteStream.of(mapper.apply(t));
            }
        });
    }

    @Override
    public ShortStream flattMapToShort(final Function mapper) {
        return flatMapToShort(new Function() {
            @Override
            public ShortStream apply(T t) {
                return ShortStream.of(mapper.apply(t));
            }
        });
    }

    @Override
    public IntStream flattMapToInt(final Function mapper) {
        return flatMapToInt(new Function() {
            @Override
            public IntStream apply(T t) {
                return IntStream.of(mapper.apply(t));
            }
        });
    }

    @Override
    public LongStream flattMapToLong(final Function mapper) {
        return flatMapToLong(new Function() {
            @Override
            public LongStream apply(T t) {
                return LongStream.of(mapper.apply(t));
            }
        });
    }

    @Override
    public FloatStream flattMapToFloat(final Function mapper) {
        return flatMapToFloat(new Function() {
            @Override
            public FloatStream apply(T t) {
                return FloatStream.of(mapper.apply(t));
            }
        });
    }

    @Override
    public DoubleStream flattMapToDouble(final Function mapper) {
        return flatMapToDouble(new Function() {
            @Override
            public DoubleStream apply(T t) {
                return DoubleStream.of(mapper.apply(t));
            }
        });
    }

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

    @Override
    @ParallelSupported
    public  EntryStream flattMapToEntry(final Function> mapper) {
        final Function>> mapper2 = new Function>>() {
            @Override
            public Stream> apply(T t) {
                return Stream.of((Map) mapper.apply(t));
            }
        };

        return flatMapToEntry(mapper2);
    }

    @Override
    @ParallelSupported
    public  EntryStream flatMappToEntry(final Function> mapper) {
        final Function>> mapper2 = new Function>>() {
            @Override
            public Stream> apply(T t) {
                return mapper.apply(t).entries();
            }
        };

        return flatMapToEntry(mapper2);
    }

    //    @Override
    //    public  EntryStream flattMapToEntry(final Function> flatValueMapper) {
    //        final Function>> flatEntryMapper = new Function>>() {
    //            @Override
    //            public Stream> apply(final T t) {
    //                final Function> entryMapper = new Function>() {
    //                    @Override
    //                    public Entry apply(V v) {
    //                        return new SimpleImmutableEntry<>(t, v);
    //                    }
    //                };
    //
    //                return Stream.of(flatValueMapper.apply(t)).map(entryMapper);
    //            }
    //        };
    //
    //        return flatMapToEntry(flatEntryMapper);
    //    }

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

        return newStream(new ObjIteratorEx() {
            private T left = null, right = null, next = null;
            private boolean hasNext = false;

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

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

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

                    if (sameRange.test(left, next)) {
                        right = next;
                    } else {
                        break;
                    }
                }

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

    @Override
    public Stream> collapse(final BiPredicate collapsible) {
        return collapse(collapsible, Suppliers. ofList()).map(listToStreamMapper());
    }

    @Override
    public > Stream collapse(final BiPredicate collapsible, final Supplier supplier) {
        return newStream(new ObjIteratorEx() {
            private final ObjIteratorEx iter = iteratorEx();
            private boolean hasNext = false;
            private T next = null;

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

            @Override
            public C next() {
                if (hasNext == false) {
                    next = iter.next();
                }

                final C c = supplier.get();
                c.add(next);

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

                return c;
            }
        }, false, null);
    }

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

        return newStream(new ObjIteratorEx() {
            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 U init, final BiFunction op) {
        final ObjIteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx() {
            private boolean hasNext = false;
            private T next = null;

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

            @Override
            public U next() {
                U res = op.apply(init, hasNext ? next : (next = iter.next()));

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

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

    @Override
    public  Stream collapse(final BiPredicate collapsible, final Supplier supplier,
            final BiConsumer accumulator) {
        final ObjIteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx() {
            private boolean hasNext = false;
            private T next = null;

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

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

                while ((hasNext = iter.hasNext())) {
                    if (collapsible.test(next, (next = iter.next()))) {
                        accumulator.accept(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 ObjIteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx() {
            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 ObjIteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx() {
            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 U init, final BiFunction accumulator) {
        final ObjIteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx() {
            private U res = init;

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

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

    @Override
    public  Stream scan(final U init, final BiFunction accumulator, boolean initIncluded) {
        if (initIncluded == false) {
            return scan(init, accumulator);
        }

        final ObjIteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx() {
            private boolean isFirst = true;
            private U res = init;

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

            @Override
            public U next() {
                if (isFirst) {
                    isFirst = false;
                    return init;
                }

                return (res = accumulator.apply(res, iter.next()));
            }
        }, false, null);
    }

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

        return sorted(comparator);
    }

    @Override
    public Stream sortedByInt(final ToIntFunction keyMapper) {
        final Comparator comparator = Comparators.comparingInt(keyMapper);

        return sorted(comparator);
    }

    @Override
    public Stream sortedByLong(final ToLongFunction keyMapper) {
        final Comparator comparator = Comparators.comparingLong(keyMapper);

        return sorted(comparator);
    }

    @Override
    public Stream sortedByDouble(final ToDoubleFunction keyMapper) {
        final Comparator comparator = Comparators.comparingDouble(keyMapper);

        return sorted(comparator);
    }

    @Override
    public Stream> split(final int chunkSize) {
        return splitToList(chunkSize).map(listToStreamMapper());
    }

    @Override
    public Stream> splitToList(final int chunkSize) {
        return split(chunkSize, Factory. ofList());
    }

    @Override
    public Stream> splitToSet(final int chunkSize) {
        return split(chunkSize, Factory. ofSet());
    }

    @Override
    public Stream> split(final Predicate predicate) {
        return splitToList(predicate).map(listToStreamMapper());
    }

    @Override
    public Stream> splitToList(final Predicate predicate) {
        return split(predicate, Suppliers. ofList());
    }

    @Override
    public Stream> splitToSet(final Predicate predicate) {
        return split(predicate, Suppliers. ofSet());
    }

    @Override
    public Stream> splitBy(final Predicate where) {
        checkArgNotNull(where);

        final IteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx>() {
            private int cursor = 0;
            private T next = null;
            private boolean hasNext = false;

            @Override
            public boolean hasNext() {
                return cursor < 2;
            }

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

                Stream result = null;

                if (cursor == 0) {
                    final List list = new ArrayList<>();

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

                        if (where.test(next)) {
                            list.add(next);
                        } else {
                            hasNext = true;
                            break;
                        }
                    }

                    result = new ArrayStream(Stream.toArray(list), 0, list.size(), sorted, cmp, null);
                } else {
                    IteratorEx iterEx = iter;

                    if (hasNext) {
                        iterEx = new ObjIteratorEx() {
                            private boolean isFirst = true;

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

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

                                if (isFirst) {
                                    isFirst = false;
                                    return next;
                                } else {
                                    return iter.next();
                                }
                            }
                        };
                    }

                    result = new IteratorStream(iterEx, sorted, cmp, null);
                }

                cursor++;

                return result;
            }

            @Override
            public long count() {
                iter.count();

                return 2 - cursor;
            }

            @Override
            public void skip(long n) {
                if (n == 0) {
                    return;
                } else if (n == 1) {
                    if (cursor == 0) {
                        while (iter.hasNext()) {
                            next = iter.next();

                            if (where.test(next) == false) {
                                hasNext = true;
                                break;
                            }
                        }
                    } else {
                        iter.skip(Long.MAX_VALUE);
                    }
                } else {
                    iter.skip(Long.MAX_VALUE);
                }

                cursor = n >= 2 ? 2 : cursor + (int) n;
            }

        }, false, null);
    }

    @Override
    public  Stream splitBy(final Predicate where, Collector collector) {
        checkArgNotNull(where, "where");
        checkArgNotNull(collector, "collector");

        final Supplier supplier = collector.supplier();
        final BiConsumer accumulator = collector.accumulator();
        final Function finisher = collector.finisher();

        final IteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx() {
            private int cursor = 0;
            private T next = null;
            private boolean hasNext = false;

            @Override
            public boolean hasNext() {
                return cursor < 2;
            }

            @Override
            public R next() {
                if (hasNext()) {
                    throw new NoSuchElementException();
                }

                final A container = supplier.get();

                if (cursor == 0) {
                    while (iter.hasNext()) {
                        next = iter.next();

                        if (where.test(next)) {
                            accumulator.accept(container, next);
                        } else {
                            hasNext = true;
                            break;
                        }
                    }
                } else {
                    if (hasNext) {
                        accumulator.accept(container, next);
                    }

                    while (iter.hasNext()) {
                        accumulator.accept(container, iter.next());
                    }
                }

                cursor++;

                return finisher.apply(container);
            }

            @Override
            public long count() {
                iter.count();

                return 2 - cursor;
            }

            @Override
            public void skip(long n) {
                if (n == 0) {
                    return;
                } else if (n == 1) {
                    if (cursor == 0) {
                        while (iter.hasNext()) {
                            next = iter.next();

                            if (where.test(next) == false) {
                                hasNext = true;
                                break;
                            }
                        }
                    } else {
                        iter.skip(Long.MAX_VALUE);
                    }
                } else {
                    iter.skip(Long.MAX_VALUE);
                }

                cursor = n >= 2 ? 2 : cursor + (int) n;
            }
        }, false, null);
    }

    @Override
    public Stream> sliding(final int windowSize, final int increment) {
        return slidingToList(windowSize, increment).map(listToStreamMapper());
    }

    @Override
    public Stream> slidingToList(final int windowSize, final int increment) {
        return sliding(windowSize, increment, Factory. ofList());
    }

    @Override
    public > Stream sliding(final int windowSize, IntFunction collectionSupplier) {
        return sliding(windowSize, 1, collectionSupplier);
    }

    @Override
    public  Stream sliding(int windowSize, Collector collector) {
        return sliding(windowSize, 1, collector);
    }

    private static final LongSupplier sysTimeGetter = new LongSupplier() {
        @Override
        public long getAsLong() {
            return System.currentTimeMillis();
        }
    };

    @Override
    public Stream> window(final Duration duration) {
        return windowToList(duration).map(listToStreamMapper());
    }

    @Override
    public Stream> window(final Duration duration, final LongSupplier startTime) {
        return window(duration, startTime, Suppliers. ofList()).map(listToStreamMapper());
    }

    @Override
    public Stream> windowToList(final Duration duration) {
        return window(duration, Suppliers. ofList());
    }

    @Override
    public Stream> windowToSet(final Duration duration) {
        return window(duration, Suppliers. ofSet());
    }

    @Override
    public > Stream window(final Duration duration, final Supplier collectionSupplier) {
        return window(duration, sysTimeGetter, collectionSupplier);
    }

    @Override
    public > Stream window(final Duration duration, final LongSupplier startTime, final Supplier collectionSupplier) {
        checkArgNotNull(duration, "duration");
        checkArgPositive(duration.toMillis(), "duration");

        return window(duration, duration.toMillis(), startTime, collectionSupplier);
    }

    @Override
    public  Stream window(Duration duration, Collector collector) {
        return window(duration, sysTimeGetter, collector);
    }

    @Override
    public  Stream window(Duration duration, final LongSupplier startTime, Collector collector) {
        checkArgNotNull(duration, "duration");
        checkArgPositive(duration.toMillis(), "duration");

        return window(duration, duration.toMillis(), startTime, collector);
    }

    @Override
    public Stream> window(final Duration duration, final long incrementInMillis) {
        return windowToList(duration, incrementInMillis).map(listToStreamMapper());
    }

    @Override
    public Stream> window(final Duration duration, final long incrementInMillis, final LongSupplier startTime) {
        return window(duration, incrementInMillis, startTime, Suppliers. ofList()).map(listToStreamMapper());
    }

    @Override
    public Stream> windowToList(final Duration duration, final long incrementInMillis) {
        return window(duration, incrementInMillis, Suppliers. ofList());
    }

    @Override
    public Stream> windowToSet(final Duration duration, final long incrementInMillis) {
        return window(duration, incrementInMillis, Suppliers. ofSet());
    }

    @Override
    public > Stream window(final Duration duration, final long incrementInMillis, final Supplier collectionSupplier) {
        return window(duration, incrementInMillis, sysTimeGetter, collectionSupplier);
    }

    @Override
    public > Stream window(final Duration duration, final long incrementInMillis, final LongSupplier startTime,
            final Supplier collectionSupplier) {
        checkArgNotNull(duration, "duration");
        checkArgPositive(duration.toMillis(), "duration");
        checkArgPositive(incrementInMillis, "incrementInMillis");
        checkArgNotNull(collectionSupplier, "collectionSupplier");

        return newStream(new ObjIteratorEx() {
            private final long durationInMillis = duration.toMillis();
            private final boolean useQueue = incrementInMillis < durationInMillis;

            private final Deque> queue = useQueue ? new ArrayDeque>() : null;
            private Iterator> queueIter;

            private ObjIteratorEx iter;
            private Timed timedNext = null;
            private T next = null;

            private long fromTime;
            private long endTime;
            private long now;

            private boolean initialized = false;

            @Override
            public boolean hasNext() {
                if (initialized == false) {
                    init();
                }

                if (useQueue) {
                    if ((queue.size() > 0 && queue.getLast().timestamp() >= endTime)
                            || (timedNext != null && timedNext.timestamp() - fromTime >= incrementInMillis)) {
                        return true;
                    } else {
                        while (iter.hasNext()) {
                            next = iter.next();
                            now = System.currentTimeMillis();

                            if (now - fromTime >= incrementInMillis) {
                                timedNext = Timed.of(next, now);
                                queue.add(timedNext);
                                return true;
                            } else if (timedNext != null) {
                                timedNext = null;
                            }
                        }

                        return false;
                    }
                } else {
                    while ((timedNext == null || timedNext.timestamp() - fromTime < incrementInMillis) && iter.hasNext()) {
                        next = iter.next();
                        now = System.currentTimeMillis();

                        if (now - fromTime >= incrementInMillis) {
                            timedNext = Timed.of(next, now);
                            break;
                        } else if (timedNext != null) {
                            timedNext = null;
                        }
                    }

                    return timedNext != null && timedNext.timestamp() - fromTime >= incrementInMillis;
                }
            }

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

                fromTime += incrementInMillis;
                endTime = fromTime + durationInMillis;

                final C result = collectionSupplier.get();

                if (useQueue) {
                    queueIter = queue.iterator();

                    while (queueIter.hasNext()) {
                        timedNext = queueIter.next();

                        if (timedNext.timestamp() < fromTime) {
                            queueIter.remove();
                        } else if (timedNext.timestamp() < endTime) {
                            result.add(timedNext.value());
                            timedNext = null;
                        } else {
                            return result;
                        }
                    }

                    while (iter.hasNext()) {
                        next = iter.next();
                        now = System.currentTimeMillis();

                        if (now >= fromTime) {
                            timedNext = Timed.of(next, now);
                            queue.add(timedNext);

                            if (timedNext.timestamp() < endTime) {
                                result.add(timedNext.value());
                                timedNext = null;
                            } else {
                                break;
                            }
                        }
                    }
                } else {
                    if (timedNext != null) {
                        if (timedNext.timestamp() < fromTime) {
                            // ignore
                        } else if (timedNext.timestamp() < endTime) {
                            result.add(timedNext.value());
                        } else {
                            return result;
                        }
                    }

                    while (iter.hasNext()) {
                        next = iter.next();
                        now = System.currentTimeMillis();

                        if (now < fromTime) {
                            // ignore
                        } else if (now < endTime) {
                            result.add(next);
                        } else {
                            timedNext = Timed.of(next, now);
                            break;
                        }
                    }
                }

                return result;
            }

            private void init() {
                if (initialized == false) {
                    initialized = true;

                    iter = iteratorEx();

                    fromTime = startTime.getAsLong() - incrementInMillis;
                    endTime = fromTime + durationInMillis;
                }
            }
        }, false, null);
    }

    @Override
    public  Stream window(final Duration duration, final long incrementInMillis, final Collector collector) {
        return window(duration, incrementInMillis, sysTimeGetter, collector);
    }

    @Override
    public  Stream window(final Duration duration, final long incrementInMillis, final LongSupplier startTime,
            final Collector collector) {
        checkArgNotNull(duration, "duration");
        checkArgPositive(duration.toMillis(), "duration");
        checkArgPositive(incrementInMillis, "incrementInMillis");
        checkArgNotNull(collector, "collector");

        return newStream(new ObjIteratorEx() {
            private final long durationInMillis = duration.toMillis();
            private final boolean useQueue = incrementInMillis < durationInMillis;

            private final Deque> queue = useQueue ? new ArrayDeque>() : null;
            private Iterator> queueIter;

            private Supplier supplier;
            private BiConsumer accumulator;
            private Function finisher;

            private ObjIteratorEx iter;
            private Timed timedNext = null;
            private T next = null;

            private long fromTime;
            private long endTime;
            private long now;

            private boolean initialized = false;

            @Override
            public boolean hasNext() {
                if (initialized == false) {
                    init();
                }

                if (useQueue) {
                    if ((queue.size() > 0 && queue.getLast().timestamp() >= endTime)
                            || (timedNext != null && timedNext.timestamp() - fromTime >= incrementInMillis)) {
                        return true;
                    } else {
                        while (iter.hasNext()) {
                            next = iter.next();
                            now = System.currentTimeMillis();

                            if (now - fromTime >= incrementInMillis) {
                                timedNext = Timed.of(next, now);
                                queue.add(timedNext);
                                return true;
                            } else if (timedNext != null) {
                                timedNext = null;
                            }
                        }

                        return false;
                    }
                } else {
                    while ((timedNext == null || timedNext.timestamp() - fromTime < incrementInMillis) && iter.hasNext()) {
                        next = iter.next();
                        now = System.currentTimeMillis();

                        if (now - fromTime >= incrementInMillis) {
                            timedNext = Timed.of(next, now);
                            break;
                        } else if (timedNext != null) {
                            timedNext = null;
                        }
                    }

                    return timedNext != null && timedNext.timestamp() - fromTime >= incrementInMillis;
                }
            }

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

                fromTime += incrementInMillis;
                endTime = fromTime + durationInMillis;

                final A container = supplier.get();

                if (useQueue) {
                    queueIter = queue.iterator();

                    while (queueIter.hasNext()) {
                        timedNext = queueIter.next();

                        if (timedNext.timestamp() < fromTime) {
                            queueIter.remove();
                        } else if (timedNext.timestamp() < endTime) {
                            accumulator.accept(container, timedNext.value());
                            timedNext = null;
                        } else {
                            return finisher.apply(container);
                        }
                    }

                    while (iter.hasNext()) {
                        next = iter.next();
                        now = System.currentTimeMillis();

                        if (now >= fromTime) {
                            timedNext = Timed.of(next, now);
                            queue.add(timedNext);

                            if (timedNext.timestamp() < endTime) {
                                accumulator.accept(container, timedNext.value());
                                timedNext = null;
                            } else {
                                break;
                            }
                        }
                    }
                } else {
                    if (timedNext != null) {
                        if (timedNext.timestamp() < fromTime) {
                            // ignore
                        } else if (timedNext.timestamp() < endTime) {
                            accumulator.accept(container, timedNext.value());
                        } else {
                            return finisher.apply(container);
                        }
                    }

                    while (iter.hasNext()) {
                        next = iter.next();
                        now = System.currentTimeMillis();

                        if (now < fromTime) {
                            // ignore
                        } else if (now < endTime) {
                            accumulator.accept(container, next);
                        } else {
                            timedNext = Timed.of(next, now);
                            break;
                        }
                    }
                }

                return finisher.apply(container);
            }

            private void init() {
                if (initialized == false) {
                    initialized = true;

                    supplier = collector.supplier();
                    accumulator = collector.accumulator();
                    finisher = collector.finisher();

                    iter = iteratorEx();

                    fromTime = startTime.getAsLong() - incrementInMillis;
                    endTime = fromTime + durationInMillis;
                }
            }
        }, false, null);
    }

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

    @Override
    public Stream> window(final int maxWindowSize, final Duration maxDuration) {
        return window(maxWindowSize, maxDuration, Suppliers. ofList()).map(listToStreamMapper());
    }

    @Override
    public Stream> window(final int maxWindowSize, final Duration maxDuration, final LongSupplier startTime) {
        return window(maxWindowSize, maxDuration, startTime, Suppliers. ofList()).map(listToStreamMapper());
    }

    @Override
    public > Stream window(final int maxWindowSize, final Duration maxDuration, final Supplier collectionSupplier) {
        return window(maxWindowSize, maxDuration, sysTimeGetter, collectionSupplier);
    }

    @Override
    public > Stream window(final int maxWindowSize, final Duration maxDuration, final LongSupplier startTime,
            final Supplier collectionSupplier) {
        checkArgPositive(maxWindowSize, "maxWindowSize");
        checkArgNotNull(maxDuration, "maxDuration");
        checkArgPositive(maxDuration.toMillis(), "maxDuration");
        checkArgNotNull(startTime, "startTime");
        checkArgNotNull(collectionSupplier, "collectionSupplier");

        return newStream(new ObjIteratorEx() {
            private final long maxDurationInMillis = maxDuration.toMillis();
            private ObjIteratorEx iter;
            private Timed timedNext = null;
            private T next = null;

            private long fromTime;
            private long endTime;
            private long now;

            private boolean initialized = false;

            @Override
            public boolean hasNext() {
                if (initialized == false) {
                    init();
                }

                while (timedNext == null && iter.hasNext()) {
                    next = iter.next();
                    now = System.currentTimeMillis();

                    if (now >= endTime) {
                        timedNext = Timed.of(next, now);
                    }
                }

                return timedNext != null;
            }

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

                fromTime = endTime;
                endTime = fromTime + maxDurationInMillis;
                int cnt = 0;
                final C result = collectionSupplier.get();

                if (timedNext != null && timedNext.timestamp() < endTime) {
                    result.add(timedNext.value());
                    timedNext = null;
                    cnt++;
                }

                if (timedNext == null) {
                    while (cnt < maxWindowSize && iter.hasNext()) {
                        next = iter.next();
                        now = System.currentTimeMillis();

                        if (now < endTime) {
                            result.add(next);
                            cnt++;
                        } else {
                            timedNext = Timed.of(next, now);
                            break;
                        }
                    }
                }

                endTime = N.min(endTime, timedNext == null ? System.currentTimeMillis() : timedNext.timestamp());

                return result;
            }

            private void init() {
                if (initialized == false) {
                    initialized = true;

                    iter = iteratorEx();

                    fromTime = startTime.getAsLong() - maxDurationInMillis;
                    endTime = fromTime + maxDurationInMillis;
                }
            }
        }, false, null);
    }

    @Override
    public  Stream window(final int maxWindowSize, final Duration maxDuration, Collector collector) {
        return window(maxWindowSize, maxDuration, sysTimeGetter, collector);
    }

    @Override
    public  Stream window(final int maxWindowSize, final Duration maxDuration, final LongSupplier startTime,
            final Collector collector) {
        checkArgPositive(maxWindowSize, "maxWindowSize");
        checkArgNotNull(maxDuration, "maxDuration");
        checkArgPositive(maxDuration.toMillis(), "maxDuration");
        checkArgNotNull(startTime, "startTime");
        checkArgNotNull(collector, "collector");

        return newStream(new ObjIteratorEx() {
            private final long maxDurationInMillis = maxDuration.toMillis();

            private Supplier supplier;
            private BiConsumer accumulator;
            private Function finisher;

            private ObjIteratorEx iter;
            private Timed timedNext = null;
            private T next = null;

            private long fromTime;
            private long endTime;
            private long now;

            private boolean initialized = false;

            @Override
            public boolean hasNext() {
                if (initialized == false) {
                    init();
                }

                while (timedNext == null && iter.hasNext()) {
                    next = iter.next();
                    now = System.currentTimeMillis();

                    if (now >= endTime) {
                        timedNext = Timed.of(next, now);
                    }
                }

                return timedNext != null;
            }

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

                fromTime = endTime;
                endTime = fromTime + maxDurationInMillis;
                int cnt = 0;
                final A container = supplier.get();

                if (timedNext != null && timedNext.timestamp() < endTime) {
                    accumulator.accept(container, timedNext.value());
                    timedNext = null;
                    cnt++;
                }

                if (timedNext == null) {
                    while (cnt < maxWindowSize && iter.hasNext()) {
                        next = iter.next();
                        now = System.currentTimeMillis();

                        if (now < endTime) {
                            accumulator.accept(container, next);
                            cnt++;
                        } else {
                            timedNext = Timed.of(next, now);
                            break;
                        }
                    }
                }

                endTime = N.min(endTime, timedNext == null ? System.currentTimeMillis() : timedNext.timestamp());

                return finisher.apply(container);
            }

            private void init() {
                if (initialized == false) {
                    initialized = true;

                    supplier = collector.supplier();
                    accumulator = collector.accumulator();
                    finisher = collector.finisher();

                    iter = iteratorEx();

                    fromTime = startTime.getAsLong() - maxDurationInMillis;
                    endTime = fromTime + maxDurationInMillis;
                }
            }
        }, false, null);
    }

    @Override
    public Stream intersperse(final T delimiter) {
        return newStream(new ObjIteratorEx() {
            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 Try.BiConsumer action) throws E {
        forEachPair(action, 1);
    }

    @Override
    public  void forEachTriple(final Try.TriConsumer action) throws E {
        forEachTriple(action, 1);
    }

    @Override
    public  Stream>> groupBy(final Function keyMapper) {
        return groupBy(keyMapper, Suppliers.> ofMap());
    }

    @Override
    public  Stream>> groupBy(final Function keyMapper, Supplier>> mapFactory) {
        return groupBy(keyMapper, Fn. identity(), mapFactory);
    }

    @Override
    public  Stream>> groupBy(Function keyMapper, Function valueMapper) {
        return groupBy(keyMapper, valueMapper, Suppliers.> ofMap());
    }

    @Override
    public  Stream>> groupBy(Function keyMapper, Function valueMapper,
            Supplier>> mapFactory) {
        return groupBy(keyMapper, valueMapper, Collectors. toList(), mapFactory);
    }

    @Override
    public  Stream> groupBy(final Function keyMapper, Collector downstream) {
        return groupBy(keyMapper, downstream, Suppliers. ofMap());
    }

    @Override
    public  Stream> groupBy(final Function keyMapper, final Collector downstream,
            final Supplier> mapFactory) {
        return groupBy(keyMapper, Fn. identity(), downstream, mapFactory);
    }

    @Override
    public  Stream> groupBy(final Function keyMapper, final Function valueMapper,
            Collector downstream) {
        return groupBy(keyMapper, valueMapper, downstream, Suppliers. ofMap());
    }

    @Override
    public  Stream> groupBy(final Function keyMapper, final Function valueMapper,
            final Collector downstream, final Supplier> mapFactory) {
        return newStream(new ObjIteratorEx>() {
            private boolean initialized = false;
            private Iterator> iter = null;

            @Override
            public boolean hasNext() {
                if (initialized == false) {
                    init();
                }

                return iter.hasNext();
            }

            @Override
            public Entry next() {
                if (initialized == false) {
                    init();
                }

                return iter.next();
            }

            private void init() {
                if (initialized == false) {
                    initialized = true;

                    iter = AbstractStream.this.toMap(keyMapper, valueMapper, downstream, mapFactory).entrySet().iterator();
                }
            }
        }, false, null);
    }

    @Override
    public  Stream> groupBy(final Function keyMapper, final Function valueMapper,
            BinaryOperator mergeFunction) {
        return groupBy(keyMapper, valueMapper, mergeFunction, Suppliers. ofMap());
    }

    @Override
    public  Stream> groupBy(final Function keyMapper, final Function valueMapper,
            final BinaryOperator mergeFunction, final Supplier> mapFactory) {
        return newStream(new ObjIteratorEx>() {
            private Iterator> iter = null;

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

            @Override
            public Entry next() {
                init();
                return iter.next();
            }

            private void init() {
                if (iter == null) {
                    iter = AbstractStream.this.toMap(keyMapper, valueMapper, mergeFunction, mapFactory).entrySet().iterator();
                }
            }
        }, false, null);
    }

    //    @Override
    //    public  Stream>> flatGroupBy(Function> flatKeyMapper) {
    //        return flatGroupBy(flatKeyMapper, Suppliers.> ofMap());
    //    }
    //
    //    @Override
    //    public  Stream>> flatGroupBy(Function> flatKeyMapper,
    //            Supplier>> mapFactory) {
    //        return flatGroupBy(flatKeyMapper, BiFunctions. returnSecond(), mapFactory);
    //    }
    //
    //    @Override
    //    public  Stream>> flatGroupBy(Function> flatKeyMapper,
    //            BiFunction valueMapper) {
    //        return flatGroupBy(flatKeyMapper, valueMapper, Suppliers.> ofMap());
    //    }
    //
    //    @Override
    //    public  Stream>> flatGroupBy(Function> flatKeyMapper,
    //            BiFunction valueMapper, Supplier>> mapFactory) {
    //        return flatGroupBy(flatKeyMapper, valueMapper, Collectors. toList(), mapFactory);
    //    }
    //
    //    @Override
    //    public  Stream> flatGroupBy(Function> flatKeyMapper, Collector downstream) {
    //        return flatGroupBy(flatKeyMapper, downstream, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public  Stream> flatGroupBy(Function> flatKeyMapper, Collector downstream,
    //            Supplier> mapFactory) {
    //        return flatGroupBy(flatKeyMapper, BiFunctions. returnSecond(), downstream, mapFactory);
    //    }
    //
    //    @Override
    //    public  Stream> flatGroupBy(Function> flatKeyMapper,
    //            BiFunction valueMapper, Collector downstream) {
    //        return flatGroupBy(flatKeyMapper, valueMapper, downstream, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public  Stream> flatGroupBy(final Function> flatKeyMapper,
    //            final BiFunction valueMapper, final Collector downstream,
    //            final Supplier> mapFactory) {
    //        return newStream(new ObjIteratorEx>() {
    //            private Iterator> iter = null;
    //
    //            @Override
    //            public boolean hasNext() {
    //                init();
    //                return iter.hasNext();
    //            }
    //
    //            @Override
    //            public Entry next() {
    //                init();
    //                return iter.next();
    //            }
    //
    //            private void init() {
    //                if (iter == null) {
    //                    iter = AbstractStream.this.flatToMap(flatKeyMapper, valueMapper, downstream, mapFactory).entrySet().iterator();
    //                }
    //            }
    //        }, false, null);
    //    }
    //
    //    @Override
    //    public  Stream> flatGroupBy(Function> flatKeyMapper,
    //            BiFunction valueMapper, BinaryOperator mergeFunction) {
    //        return flatGroupBy(flatKeyMapper, valueMapper, mergeFunction, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public  Stream> flatGroupBy(final Function> flatKeyMapper,
    //            final BiFunction valueMapper, final BinaryOperator mergeFunction,
    //            final Supplier> mapFactory) {
    //        return newStream(new ObjIteratorEx>() {
    //            private Iterator> iter = null;
    //
    //            @Override
    //            public boolean hasNext() {
    //                init();
    //                return iter.hasNext();
    //            }
    //
    //            @Override
    //            public Entry next() {
    //                init();
    //                return iter.next();
    //            }
    //
    //            private void init() {
    //                if (iter == null) {
    //                    iter = AbstractStream.this.flatToMap(flatKeyMapper, valueMapper, mergeFunction, mapFactory).entrySet().iterator();
    //                }
    //            }
    //        }, false, null);
    //    }
    //
    //    @Override
    //    public  Stream>> flattGroupBy(Function> flatKeyMapper) {
    //        return flattGroupBy(flatKeyMapper, Suppliers.> ofMap());
    //    }
    //
    //    @Override
    //    public  Stream>> flattGroupBy(Function> flatKeyMapper,
    //            Supplier>> mapFactory) {
    //        return flattGroupBy(flatKeyMapper, BiFunctions. returnSecond(), mapFactory);
    //    }
    //
    //    @Override
    //    public  Stream>> flattGroupBy(Function> flatKeyMapper,
    //            BiFunction valueMapper) {
    //        return flattGroupBy(flatKeyMapper, valueMapper, Suppliers.> ofMap());
    //    }
    //
    //    @Override
    //    public  Stream>> flattGroupBy(Function> flatKeyMapper,
    //            BiFunction valueMapper, Supplier>> mapFactory) {
    //        return flattGroupBy(flatKeyMapper, valueMapper, Collectors. toList(), mapFactory);
    //    }
    //
    //    @Override
    //    public  Stream> flattGroupBy(Function> flatKeyMapper,
    //            Collector downstream) {
    //        return flattGroupBy(flatKeyMapper, downstream, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public  Stream> flattGroupBy(Function> flatKeyMapper,
    //            Collector downstream, Supplier> mapFactory) {
    //        return flattGroupBy(flatKeyMapper, BiFunctions. returnSecond(), downstream, mapFactory);
    //    }
    //
    //    @Override
    //    public  Stream> flattGroupBy(Function> flatKeyMapper,
    //            BiFunction valueMapper, Collector downstream) {
    //        return flattGroupBy(flatKeyMapper, valueMapper, downstream, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public  Stream> flattGroupBy(final Function> flatKeyMapper,
    //            final BiFunction valueMapper, final Collector downstream,
    //            final Supplier> mapFactory) {
    //        return newStream(new ObjIteratorEx>() {
    //            private Iterator> iter = null;
    //
    //            @Override
    //            public boolean hasNext() {
    //                init();
    //                return iter.hasNext();
    //            }
    //
    //            @Override
    //            public Entry next() {
    //                init();
    //                return iter.next();
    //            }
    //
    //            private void init() {
    //                if (iter == null) {
    //                    iter = AbstractStream.this.flattToMap(flatKeyMapper, valueMapper, downstream, mapFactory).entrySet().iterator();
    //                }
    //            }
    //        }, false, null);
    //    }
    //
    //    @Override
    //    public  Stream> flattGroupBy(Function> flatKeyMapper,
    //            BiFunction valueMapper, BinaryOperator mergeFunction) {
    //        return flattGroupBy(flatKeyMapper, valueMapper, mergeFunction, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public  Stream> flattGroupBy(final Function> flatKeyMapper,
    //            final BiFunction valueMapper, final BinaryOperator mergeFunction,
    //            final Supplier> mapFactory) {
    //        return newStream(new ObjIteratorEx>() {
    //            private Iterator> iter = null;
    //
    //            @Override
    //            public boolean hasNext() {
    //                init();
    //                return iter.hasNext();
    //            }
    //
    //            @Override
    //            public Entry next() {
    //                init();
    //                return iter.next();
    //            }
    //
    //            private void init() {
    //                if (iter == null) {
    //                    iter = AbstractStream.this.flattToMap(flatKeyMapper, valueMapper, mergeFunction, mapFactory).entrySet().iterator();
    //                }
    //            }
    //        }, false, null);
    //    }

    @Override
    public Stream>> partitionBy(Predicate predicate) {
        return partitionBy(predicate, Collectors. toList());
    }

    @Override
    public  Stream> partitionBy(final Predicate predicate, final Collector downstream) {
        return newStream(new ObjIteratorEx>() {
            private Iterator> iter = null;

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

            @Override
            public Entry next() {
                init();
                return iter.next();
            }

            private void init() {
                if (iter == null) {
                    iter = AbstractStream.this.partitionTo(predicate, downstream).entrySet().iterator();
                }
            }
        }, false, null);
    }

    @Override
    public EntryStream> partitionByToEntry(Predicate predicate) {
        return partitionByToEntry(predicate, Collectors. toList());
    }

    @Override
    public  EntryStream partitionByToEntry(Predicate predicate, Collector downstream) {
        return partitionBy(predicate, downstream).mapToEntry(Fn.> identity());
    }

    @Override
    public  EntryStream> groupByToEntry(Function keyMapper) {
        return groupByToEntry(keyMapper, Suppliers.> ofMap());
    }

    @Override
    public  EntryStream> groupByToEntry(Function keyMapper, Supplier>> mapFactory) {
        return groupByToEntry(keyMapper, Fn. identity(), mapFactory);
    }

    @Override
    public  EntryStream> groupByToEntry(Function keyMapper, Function valueMapper) {
        return groupByToEntry(keyMapper, valueMapper, Suppliers.> ofMap());
    }

    @Override
    public  EntryStream> groupByToEntry(Function keyMapper, Function valueMapper,
            Supplier>> mapFactory) {
        return groupBy(keyMapper, valueMapper, mapFactory).mapToEntry(Fn.>> identity());
    }

    @Override
    public  EntryStream groupByToEntry(Function keyMapper, Collector downstream) {
        return groupByToEntry(keyMapper, downstream, Suppliers. ofMap());
    }

    @Override
    public  EntryStream groupByToEntry(Function keyMapper, Collector downstream,
            Supplier> mapFactory) {
        return groupBy(keyMapper, downstream, mapFactory).mapToEntry(Fn.> identity());
    }

    @Override
    public  EntryStream groupByToEntry(final Function keyMapper, final Function valueMapper,
            Collector downstream) {
        return groupByToEntry(keyMapper, valueMapper, downstream, Suppliers. ofMap());
    }

    @Override
    public  EntryStream groupByToEntry(final Function keyMapper, final Function valueMapper,
            Collector downstream, Supplier> mapFactory) {
        return groupBy(keyMapper, valueMapper, downstream, mapFactory).mapToEntry(Fn.> identity());
    }

    @Override
    public  EntryStream groupByToEntry(Function keyMapper, Function valueMapper,
            BinaryOperator mergeFunction) {
        return groupByToEntry(keyMapper, valueMapper, mergeFunction, Suppliers. ofMap());
    }

    @Override
    public  EntryStream groupByToEntry(Function keyMapper, Function valueMapper,
            BinaryOperator mergeFunction, Supplier> mapFactory) {
        return groupBy(keyMapper, valueMapper, mergeFunction, mapFactory).mapToEntry(Fn.> identity());
    }

    @Override
    public  Map toMap(Function keyMapper, Function valueMapper) {
        return toMap(keyMapper, valueMapper, Suppliers. ofMap());
    }

    @Override
    public > M toMap(Function keyMapper, Function valueMapper,
            Supplier mapFactory) {
        return toMap(keyMapper, valueMapper, Fn. throwingMerger(), mapFactory);
    }

    @Override
    public  Map toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction) {
        return toMap(keyMapper, valueMapper, mergeFunction, Suppliers. ofMap());
    }

    @Override
    public  Map toMap(Function keyMapper, Collector downstream) {
        return toMap(keyMapper, downstream, Suppliers. ofMap());
    }

    @Override
    public  Map toMap(Function keyMapper, Function valueMapper,
            Collector downstream) {
        return toMap(keyMapper, valueMapper, downstream, Suppliers. ofMap());
    }

    @Override
    public > M toMap(final Function keyMapper, final Collector downstream,
            final Supplier mapFactory) {
        return toMap(keyMapper, Fn. identity(), downstream, mapFactory);
    }

    //    @Override
    //    public  Map flatToMap(Function> flatKeyMapper,
    //            BiFunction valueMapper) {
    //        return flatToMap(flatKeyMapper, valueMapper, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public  Map flatToMap(Function> flatKeyMapper,
    //            BiFunction valueMapper, BinaryOperator mergeFunction) {
    //        return flatToMap(flatKeyMapper, valueMapper, mergeFunction, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public > M flatToMap(Function> flatKeyMapper,
    //            BiFunction valueMapper, Supplier mapFactory) {
    //        return flatToMap(flatKeyMapper, valueMapper, Fn. throwingMerger(), mapFactory);
    //    }
    //
    //    @Override
    //    public  Map flatToMap(Function> flatKeyMapper, Collector downstream) {
    //        return flatToMap(flatKeyMapper, downstream, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public > M flatToMap(Function> flatKeyMapper, Collector downstream,
    //            Supplier mapFactory) {
    //        return flatToMap(flatKeyMapper, BiFunctions. returnSecond(), downstream, mapFactory);
    //    }
    //
    //    @Override
    //    public  Map flatToMap(Function> flatKeyMapper,
    //            BiFunction valueMapper, Collector downstream) {
    //        return flatToMap(flatKeyMapper, valueMapper, downstream, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public  Map flattToMap(Function> flatKeyMapper, Collector downstream) {
    //        return flattToMap(flatKeyMapper, downstream, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public > M flattToMap(Function> flatKeyMapper,
    //            Collector downstream, Supplier mapFactory) {
    //        return flattToMap(flatKeyMapper, BiFunctions. returnSecond(), downstream, mapFactory);
    //    }
    //
    //    @Override
    //    public  Map flattToMap(Function> flatKeyMapper,
    //            BiFunction valueMapper) {
    //        return flattToMap(flatKeyMapper, valueMapper, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public  Map flattToMap(Function> flatKeyMapper,
    //            BiFunction valueMapper, BinaryOperator mergeFunction) {
    //        return flattToMap(flatKeyMapper, valueMapper, mergeFunction, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public > M flattToMap(Function> flatKeyMapper,
    //            BiFunction valueMapper, Supplier mapFactory) {
    //        return flattToMap(flatKeyMapper, valueMapper, Fn. throwingMerger(), mapFactory);
    //    }
    //
    //    @Override
    //    public > M flattToMap(final Function> flatKeyMapper,
    //            final BiFunction valueMapper, final BinaryOperator mergeFunction, final Supplier mapFactory) {
    //        return flatToMap(new Function>() {
    //            @Override
    //            public Stream apply(T t) {
    //                return Stream.of(flatKeyMapper.apply(t));
    //            }
    //        }, valueMapper, mergeFunction, mapFactory);
    //    }
    //
    //    @Override
    //    public  Map flattToMap(Function> flatKeyMapper,
    //            BiFunction valueMapper, Collector downstream) {
    //        return flattToMap(flatKeyMapper, valueMapper, downstream, Suppliers. ofMap());
    //    }
    //
    //    @Override
    //    public > M flattToMap(final Function> flatKeyMapper,
    //            final BiFunction valueMapper, final Collector downstream,
    //            final Supplier mapFactory) {
    //        return flatToMap(new Function>() {
    //            @Override
    //            public Stream apply(T t) {
    //                return Stream.of(flatKeyMapper.apply(t));
    //            }
    //        }, valueMapper, downstream, mapFactory);
    //    }

    @Override
    public  Map> groupTo(Function keyMapper) {
        return groupTo(keyMapper, Suppliers.> ofMap());
    }

    @Override
    public >> M groupTo(Function keyMapper, Supplier mapFactory) {
        return toMap(keyMapper, Collectors. toList(), mapFactory);
    }

    @Override
    public  Map> groupTo(Function keyMapper, Function valueMapper) {
        return groupTo(keyMapper, valueMapper, Suppliers.> ofMap());
    }

    @Override
    public >> M groupTo(Function keyMapper, Function valueMapper,
            Supplier mapFactory) {
        return toMap(keyMapper, valueMapper, Collectors. toList(), mapFactory);
    }

    //    @Override
    //    public  Map> flatGroupTo(Function> flatKeyMapper) {
    //        return flatGroupTo(flatKeyMapper, Suppliers.> ofMap());
    //    }
    //
    //    @Override
    //    public >> M flatGroupTo(Function> flatKeyMapper, Supplier mapFactory) {
    //        return flatGroupTo(flatKeyMapper, BiFunctions. returnSecond(), mapFactory);
    //    }
    //
    //    @Override
    //    public  Map> flatGroupTo(Function> flatKeyMapper,
    //            BiFunction valueMapper) {
    //        return flatGroupTo(flatKeyMapper, valueMapper, Suppliers.> ofMap());
    //    }
    //
    //    @Override
    //    public >> M flatGroupTo(Function> flatKeyMapper,
    //            BiFunction valueMapper, Supplier mapFactory) {
    //        return flatToMap(flatKeyMapper, valueMapper, Collectors. toList(), mapFactory);
    //    }
    //
    //    @Override
    //    public  Map> flattGroupTo(Function> flatKeyMapper) {
    //        return flattGroupTo(flatKeyMapper, Suppliers.> ofMap());
    //    }
    //
    //    @Override
    //    public >> M flattGroupTo(Function> flatKeyMapper,
    //            Supplier mapFactory) {
    //        return flattGroupTo(flatKeyMapper, BiFunctions. returnSecond(), mapFactory);
    //    }
    //
    //    @Override
    //    public  Map> flattGroupTo(Function> flatKeyMapper,
    //            BiFunction valueMapper) {
    //        return flattGroupTo(flatKeyMapper, valueMapper, Suppliers.> ofMap());
    //    }
    //
    //    @Override
    //    public >> M flattGroupTo(Function> flatKeyMapper,
    //            BiFunction valueMapper, Supplier mapFactory) {
    //        return flattToMap(flatKeyMapper, valueMapper, Collectors. toList(), mapFactory);
    //    }

    @Override
    public Map> partitionTo(Predicate predicate) {
        return partitionTo(predicate, Collectors. toList());
    }

    @Override
    public  Map partitionTo(Predicate predicate, Collector downstream) {
        return collect(Collectors.partitioningBy(predicate, downstream));
    }

    @Override
    public  ListMultimap toMultimap(Function keyMapper) {
        return toMultimap(keyMapper, Suppliers. ofListMultimap());
    }

    @Override
    public , M extends Multimap> M toMultimap(Function keyMapper,
            Supplier mapFactory) {
        return toMultimap(keyMapper, Fn. identity(), mapFactory);
    }

    @Override
    public  ListMultimap toMultimap(Function keyMapper, Function valueMapper) {
        return toMultimap(keyMapper, valueMapper, Suppliers. ofListMultimap());
    }

    @Override
    public  ListMultimap flatToMultimap(Function> flatKeyMapper) {
        return flatToMultimap(flatKeyMapper, Suppliers. ofListMultimap());
    }

    @Override
    public , M extends Multimap> M flatToMultimap(Function> flatKeyMapper,
            Supplier mapFactory) {
        return flatToMultimap(flatKeyMapper, BiFunctions. returnSecond(), mapFactory);
    }

    @Override
    public  ListMultimap flatToMultimap(Function> flatKeyMapper,
            BiFunction valueMapper) {
        return flatToMultimap(flatKeyMapper, valueMapper, Suppliers. ofListMultimap());
    }

    @Override
    public  ListMultimap flattToMultimap(Function> flatKeyMapper) {
        return flattToMultimap(flatKeyMapper, Suppliers. ofListMultimap());
    }

    @Override
    public , M extends Multimap> M flattToMultimap(Function> flatKeyMapper,
            Supplier mapFactory) {
        return flattToMultimap(flatKeyMapper, BiFunctions. returnSecond(), mapFactory);
    }

    @Override
    public  ListMultimap flattToMultimap(Function> flatKeyMapper,
            BiFunction valueMapper) {
        return flattToMultimap(flatKeyMapper, valueMapper, Suppliers. ofListMultimap());
    }

    @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.averagingIntt(mapper));
    }

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

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

    @Override
    public  Stream> innerJoin(final Collection b, final Function leftKeyMapper, final Function rightKeyMapper) {
        final ListMultimap rightKeyMap = ListMultimap.from(b, rightKeyMapper);

        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                return Stream.of(rightKeyMap.get(leftKeyMapper.apply(t))).map(new Function>() {
                    @Override
                    public Pair apply(U u) {
                        return Pair.of(t, u);
                    }
                });
            }
        });
    }

    @Override
    public  Stream> innerJoin(final Collection b, final BiPredicate predicate) {
        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                return Stream.of(b).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 joinedRights = new IdentityHashMap<>();
        final boolean isParallelStream = this.isParallel();

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

                return N.isNullOrEmpty(values) ? Stream.of(Pair.of(t, (U) null)) : Stream.of(values).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 Map joinedRights = new IdentityHashMap<>();
        final boolean isParallelStream = this.isParallel();

        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                final MutableBoolean joined = MutableBoolean.of(false);

                return Stream.of(b).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 BooleanSupplier() {
                    @Override
                    public boolean getAsBoolean() {
                        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);

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

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

    @Override
    public  Stream> leftJoin(final Collection b, final BiPredicate predicate) {
        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                final MutableBoolean joined = MutableBoolean.of(false);

                return Stream.of(b).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 BooleanSupplier() {
                    @Override
                    public boolean getAsBoolean() {
                        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 joinedRights = new IdentityHashMap<>();
        final boolean isParallelStream = this.isParallel();

        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                return Stream.of(rightKeyMap.get(leftKeyMapper.apply(t))).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 Map joinedRights = new IdentityHashMap<>();
        final boolean isParallelStream = this.isParallel();

        return flatMap(new Function>>() {
            @Override
            public Stream> apply(final T t) {
                return Stream.of(b).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  Optional findAny(final Try.Predicate predicate) throws E {
        return findFirst(predicate);
    }

    @Override
    public  Optional findFirstOrLast(final Try.Predicate predicateForFirst,
            final Try.Predicate predicateForLast) throws E, E2 {
        assertNotClosed();

        try {
            final ObjIteratorEx iter = iteratorEx();
            T last = (T) NONE;
            T next = null;

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

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

            return last == NONE ? (Optional) Optional.empty() : Optional.of(last);
        } finally {
            close();
        }
    }

    @Override
    public  Optional findFirstOrLast(final U init,
            final Try.BiPredicate predicateForFirst, final Try.BiPredicate predicateForLast) throws E, E2 {
        assertNotClosed();

        try {
            final ObjIteratorEx iter = iteratorEx();
            T last = (T) NONE;
            T next = null;

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

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

            return last == NONE ? (Optional) Optional.empty() : Optional.of(last);
        } finally {
            close();
        }
    }

    @Override
    public  Optional findFirstOrLast(final Function preFunc,
            final Try.BiPredicate predicateForFirst, final Try.BiPredicate predicateForLast) throws E, E2 {
        assertNotClosed();

        try {
            final ObjIteratorEx iter = iteratorEx();
            U init = null;
            T last = (T) NONE;
            T next = null;

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

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

            return last == NONE ? (Optional) Optional.empty() : Optional.of(last);
        } finally {
            close();
        }
    }

    @Override
    @SafeVarargs
    public final boolean containsAll(final T... a) {
        assertNotClosed();

        try {
            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));
            }
        } finally {
            close();
        }
    }

    @Override
    public boolean containsAll(final Collection c) {
        assertNotClosed();

        try {
            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();
            }
        } finally {
            close();
        }
    }

    @Override
    public Optional first() {
        assertNotClosed();

        try {
            final Iterator iter = this.iterator();

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

            return Optional.of(iter.next());
        } finally {
            close();
        }
    }

    @Override
    public Optional last() {
        assertNotClosed();

        try {
            final Iterator iter = this.iterator();

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

            T next = iter.next();

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

            return Optional.of(next);
        } finally {
            close();
        }
    }

    @Override
    public Optional onlyOne() throws DuplicatedResultException {
        assertNotClosed();

        try {
            final Iterator iter = this.iteratorEx();

            final Optional result = iter.hasNext() ? Optional.of(iter.next()) : Optional. empty();

            if (result.isPresent() && iter.hasNext()) {
                throw new DuplicatedResultException("There are at least two elements: " + Strings.concat(result.get(), ", ", iter.next()));
            }

            return result;
        } finally {
            close();
        }
    }

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

    @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() {
        return newStream(new ObjIteratorEx() {
            private boolean initialized = false;
            private T[] aar;
            private int cursor;

            @Override
            public boolean hasNext() {
                if (initialized == false) {
                    init();
                }

                return cursor > 0;
            }

            @Override
            public T next() {
                if (initialized == false) {
                    init();
                }

                if (cursor <= 0) {
                    throw new NoSuchElementException();
                }

                return aar[--cursor];
            }

            @Override
            public long count() {
                if (initialized == false) {
                    init();
                }

                return cursor;
            }

            @Override
            public void skip(long n) {
                if (initialized == false) {
                    init();
                }

                cursor = n < cursor ? cursor - (int) n : 0;
            }

            @Override
            public  A[] toArray(A[] a) {
                if (initialized == false) {
                    init();
                }

                a = a.length >= cursor ? a : (A[]) N.newArray(a.getClass().getComponentType(), cursor);

                for (int i = 0; i < cursor; i++) {
                    a[i] = (A) aar[cursor - i - 1];
                }

                return a;
            }

            private void init() {
                if (initialized == false) {
                    initialized = true;
                    aar = (T[]) AbstractStream.this.toArray();
                    cursor = aar.length;
                }
            }
        }, false, null);
    }

    @Override
    public Stream shuffled(final Random rnd) {
        return lazyLoad(new Function() {
            @Override
            public Object[] apply(final Object[] a) {
                N.shuffle(a, rnd);
                return a;
            }
        }, false, null);
    }

    @Override
    public Stream rotated(final int distance) {
        return newStream(new ObjIteratorEx() {
            private boolean initialized = false;
            private T[] aar;
            private int len;
            private int start;
            private int cnt = 0;

            @Override
            public boolean hasNext() {
                if (initialized == false) {
                    init();
                }

                return cnt < len;
            }

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

                return aar[(start + cnt++) % len];
            }

            @Override
            public long count() {
                if (initialized == false) {
                    init();
                }

                return len - cnt;
            }

            @Override
            public void skip(long n) {
                if (initialized == false) {
                    init();
                }

                cnt = n < len - cnt ? cnt + (int) n : len;
            }

            @Override
            public  A[] toArray(A[] a) {
                if (initialized == false) {
                    init();
                }

                a = a.length >= len - cnt ? a : (A[]) N.newArray(a.getClass().getComponentType(), len - cnt);

                for (int i = cnt; i < len; i++) {
                    a[i - cnt] = (A) aar[(start + i) % len];
                }

                return a;
            }

            private void init() {
                if (initialized == false) {
                    initialized = true;
                    aar = (T[]) AbstractStream.this.toArray();
                    len = aar.length;

                    if (len > 0) {
                        start = distance % len;

                        if (start < 0) {
                            start += len;
                        }

                        start = len - start;
                    }
                }
            }
        }, distance == 0 && sorted, distance == 0 ? cmp : null);
    }

    @Override
    public Stream sorted() {
        return sorted(NATURAL_COMPARATOR);
    }

    @Override
    public Stream reverseSorted() {
        return sorted(REVERSED_COMPARATOR);
    }

    @Override
    public Stream sorted(final Comparator comparator) {
        final Comparator cmp = comparator == null ? NATURAL_COMPARATOR : comparator;

        if (sorted && cmp == this.cmp) {
            return newStream(iterator(), sorted, cmp);
        }

        return lazyLoad(new Function() {
            @Override
            public Object[] apply(final Object[] a) {
                if (isParallel()) {
                    N.parallelSort((T[]) a, cmp);
                } else {
                    N.sort((T[]) a, cmp);
                }

                return a;
            }
        }, true, cmp);
    }

    private Stream lazyLoad(final Function op, final boolean sorted, final Comparator cmp) {
        return newStream(new ObjIteratorEx() {
            private boolean initialized = false;
            private T[] aar;
            private int cursor = 0;
            private int len;

            @Override
            public boolean hasNext() {
                if (initialized == false) {
                    init();
                }

                return cursor < len;
            }

            @Override
            public T next() {
                if (initialized == false) {
                    init();
                }

                if (cursor >= len) {
                    throw new NoSuchElementException();
                }

                return aar[cursor++];
            }

            @Override
            public long count() {
                if (initialized == false) {
                    init();
                }

                return len - cursor;
            }

            @Override
            public void skip(long n) {
                if (initialized == false) {
                    init();
                }

                cursor = n > len - cursor ? len : cursor + (int) n;
            }

            @Override
            public  A[] toArray(A[] a) {
                if (initialized == false) {
                    init();
                }

                a = a.length >= (len - cursor) ? a : (A[]) N.newArray(a.getClass().getComponentType(), (len - cursor));

                for (int i = cursor; i < len; i++) {
                    a[i - cursor] = (A) aar[i];
                }

                return a;
            }

            private void init() {
                if (initialized == false) {
                    initialized = true;
                    aar = (T[]) op.apply(AbstractStream.this.toArray());
                    len = aar.length;
                }
            }
        }, sorted, cmp);
    }

    @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 keyMapper) {
        //    final Set set = new HashSet<>();
        //
        //    final Predicate predicate = isParallel() ? new Predicate() {
        //        @Override
        //        public boolean test(T value) {
        //            final Object key = hashKey(keyMapper.apply(value));
        //
        //            synchronized (set) {
        //                return set.add(key);
        //            }
        //        }
        //    } : new Predicate() {
        //        @Override
        //        public boolean test(T value) {
        //            return set.add(hashKey(keyMapper.apply(value)));
        //        }
        //    };

        final Predicate predicate = isParallel() ? new Predicate() {
            private final ConcurrentHashMap map = new ConcurrentHashMap<>();

            @Override
            public boolean test(T value) {
                return map.put(hashKey(keyMapper.apply(value)), StreamBase.NONE) == null;
            }
        } : new Predicate() {
            private final Set set = new HashSet<>();

            @Override
            public boolean test(T value) {
                return set.add(hashKey(keyMapper.apply(value)));
            }
        };

        return filter(predicate);
    }

    @Override
    public Stream top(int n) {
        return top(n, NATURAL_COMPARATOR);
    }

    @Override
    public Optional> percentiles() {
        assertNotClosed();

        try {
            final Object[] a = sorted().toArray();

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

            return Optional.of((Map) N.percentiles(a));
        } finally {
            close();
        }
    }

    @Override
    public Optional> percentiles(Comparator comparator) {
        assertNotClosed();

        try {
            final Object[] a = sorted(comparator).toArray();

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

            return Optional.of((Map) N.percentiles(a));
        } finally {
            close();
        }
    }

    //    @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) {
            @SuppressWarnings("resource")
            final ArrayStream s = ((ArrayStream) this);
            final int count = s.toIndex - s.fromIndex;

            return newStream(IntStream.rangeClosed(0, 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) {
            @SuppressWarnings("resource")
            final ArrayStream s = ((ArrayStream) this);
            final int count = s.toIndex - s.fromIndex;
            checkFromIndexSize(0, len, count);

            if (len == 0) {
                return newStream(N.asArray(N. emptyList()), 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 = s.elements;
                final int fromIndex = s.fromIndex;
                final int toIndex = s.toIndex;

                return newStream(new ObjIteratorEx>() {
                    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> combinations(final int len, final boolean repeat) {
        if (repeat == false) {
            return combinations(len);
        } else {
            return newStream(new ObjIteratorEx>() {
                private boolean initialized = false;
                private List> list = null;
                private int size = 0;
                private int cursor = 0;

                @Override
                public boolean hasNext() {
                    if (initialized == false) {
                        init();
                    }

                    return cursor < size;
                }

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

                    return list.get(cursor++);
                }

                @Override
                public void skip(long n) {
                    if (initialized == false) {
                        init();
                    }

                    cursor = n <= size - cursor ? cursor + (int) n : size;
                }

                @Override
                public long count() {
                    if (initialized == false) {
                        init();
                    }

                    return size - cursor;
                }

                private void init() {
                    if (initialized == false) {
                        initialized = true;
                        list = Iterables.cartesianProduct(N.repeat(AbstractStream.this.toList(), len));
                        size = list.size();
                    }
                }
            }, false, null);
        }
    }

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

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

    @Override
    public Stream> orderedPermutations(Comparator comparator) {
        final Iterator> iter = PermutationIterator.ordered(toList(), comparator == null ? NATURAL_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(new ObjIteratorEx>() {
            private boolean initialized = false;
            private List> list = null;
            private int size = 0;
            private int cursor = 0;

            @Override
            public boolean hasNext() {
                if (initialized == false) {
                    init();
                }

                return cursor < size;
            }

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

                return list.get(cursor++);
            }

            @Override
            public void skip(long n) {
                if (initialized == false) {
                    init();
                }

                cursor = n <= size - cursor ? cursor + (int) n : size;
            }

            @Override
            public long count() {
                if (initialized == false) {
                    init();
                }

                return size - cursor;
            }

            private void init() {
                if (initialized == false) {
                    initialized = true;
                    list = Iterables.cartesianProduct(cList);
                    size = list.size();
                }
            }

        }, false, null);
    }

    @Override
    public  A[] toArray(IntFunction generator) {
        assertNotClosed();

        try {
            final Object[] src = toArray();
            final A[] res = generator.apply(src.length);
            System.arraycopy(src, 0, res, 0, src.length);
            return res;
        } finally {
            close();
        }
    }

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

    @Override
    public DataSet toDataSet(boolean isFirstTitle) {
        assertNotClosed();

        try {
            if (isFirstTitle) {
                final ObjIterator iter = this.iterator();

                if (iter.hasNext() == false) {
                    return N.newDataSet(new ArrayList(0), new ArrayList>(0));
                }

                final T header = iter.next();
                final Type type = N.typeOf(header.getClass());
                List columnNames = null;

                if (type.isArray()) {
                    final Object[] a = (Object[]) header;
                    columnNames = new ArrayList<>(a.length);

                    for (Object e : a) {
                        columnNames.add(N.stringOf(e));
                    }
                } else if (type.isCollection()) {
                    final Collection c = (Collection) header;
                    columnNames = new ArrayList<>(c.size());

                    for (Object e : c) {
                        columnNames.add(N.stringOf(e));
                    }
                } else {
                    throw new IllegalArgumentException("Unsupported header type: " + type.name());
                }

                return N.newDataSet(columnNames, Iterators.toList(iter));
            } else {
                return toDataSet(null);
            }
        } finally {
            close();
        }
    }

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

    @Override
    public String join(CharSequence delimiter, CharSequence prefix, CharSequence suffix) {
        assertNotClosed();

        try {
            final Joiner joiner = Joiner.with(delimiter, prefix, suffix).reuseCachedBuffer(true);
            final IteratorEx iter = this.iteratorEx();

            while (iter.hasNext()) {
                joiner.append(iter.next());
            }

            return joiner.toString();
        } finally {
            close();
        }
    }

    @Override
    public boolean hasDuplicates() {
        assertNotClosed();

        try {
            final Set set = new HashSet<>();
            final Iterator iter = iterator();

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

            return false;
        } finally {
            close();
        }
    }

    @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.from(collector));
    }

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

    @Override
    public  RR collectAndThen(java.util.stream.Collector downstream, java.util.function.Function func) {
        return func.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 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 appendAlll(Collection> cs) {
        return append(Stream.of(cs).flattMap(Fn.> identity()));
    }

    @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 prependAlll(Collection> cs) {
        return prepend(Stream.of(cs).flattMap(Fn.> identity()));
    }

    @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  void forEach(Try.Consumer action) throws E {
        forEach(action, Fn.emptyAction());
    }

    @Override
    public long persist(File file, Try.Function toLine) throws IOException {
        final Writer writer = new FileWriter(file);

        try {
            return persist(writer, toLine);
        } finally {
            IOUtil.close(writer);
        }
    }

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

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

    @Override
    public long persist(Writer writer, Try.Function toLine) throws IOException {
        assertNotClosed();

        try {
            boolean isBufferedWriter = writer instanceof BufferedWriter || writer instanceof java.io.BufferedWriter;
            final Writer bw = isBufferedWriter ? writer : Objectory.createBufferedWriter(writer);
            final Iterator iter = iterator();
            long cnt = 0;

            try {
                while (iter.hasNext()) {
                    bw.write(toLine.apply(iter.next()));
                    bw.write(IOUtil.LINE_SEPARATOR);
                    cnt++;
                }

                bw.flush();
            } finally {
                if (!isBufferedWriter) {
                    Objectory.recycle((BufferedWriter) bw);
                }
            }

            return cnt;
        } finally {
            close();
        }
    }

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

        try {
            stmt = conn.prepareStatement(insertSQL);

            return persist(stmt, batchSize, batchInterval, stmtSetter);
        } finally {
            JdbcUtil.closeQuietly(stmt);
        }
    }

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

        try {
            final Iterator iter = iterator();

            long cnt = 0;
            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();
            }

            return cnt;
        } finally {
            close();
        }
    }

    /**
     * @param keyMapper
     * @param valueMapper
     * @return
     * @deprecated
     */
    @Deprecated
    @Override
    public  Stream> mapToDisposableEntry(final Function keyMapper,
            final Function valueMapper) {
        checkState(isParallel() == false, "mapToDisposableEntry can't be applied to parallel stream");

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

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

                return entry;
            }
        };

        return map(mapper);
    }
}