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

com.landawn.abacus.util.ExceptionalStream Maven / Gradle / Ivy

Go to download

A general programming library in Java/Android. It's easy to learn and simple to use with concise and powerful APIs.

There is a newer version: 2.1.12
Show newest version
/*
 * Copyright (C) 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;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.security.SecureRandom;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Random;
import java.util.Set;

import com.landawn.abacus.DataSet;
import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.annotation.IntermediateOp;
import com.landawn.abacus.annotation.SequentialOnly;
import com.landawn.abacus.annotation.TerminalOp;
import com.landawn.abacus.exception.DuplicatedResultException;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.util.Fn.Factory;
import com.landawn.abacus.util.Fn.Fnn;
import com.landawn.abacus.util.Fn.Suppliers;
import com.landawn.abacus.util.If.OrElse;
import com.landawn.abacus.util.StringUtil.Strings;
import com.landawn.abacus.util.Tuple.Tuple2;
import com.landawn.abacus.util.u.Holder;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.u.OptionalDouble;
import com.landawn.abacus.util.u.OptionalLong;
import com.landawn.abacus.util.function.BiConsumer;
import com.landawn.abacus.util.function.Function;
import com.landawn.abacus.util.function.IntFunction;
import com.landawn.abacus.util.function.Supplier;
import com.landawn.abacus.util.stream.Collector;
import com.landawn.abacus.util.stream.Collectors;

// TODO: Auto-generated Javadoc
/**
 * The Stream will be automatically closed after execution(A terminal method is executed/triggered).
 *
 * @author Haiyang Li
 * @param 
 * @param 
 * @since 1.3
 */
@SequentialOnly
public class ExceptionalStream implements AutoCloseable {

    /** The Constant logger. */
    static final Logger logger = LoggerFactory.getLogger(ExceptionalStream.class);

    static final Random RAND = new SecureRandom();

    /** The Constant KK. */
    static final Throwables.Function, Object>, Object, Exception> KK = new Throwables.Function, Object>, Object, Exception>() {
        @Override
        public Object apply(Map.Entry, Object> t) throws Exception {
            return t.getKey().val();
        }
    };

    /** The elements. */
    private final ExceptionalIterator elements;

    /** The sorted. */
    private final boolean sorted;

    /** The comparator. */
    private final Comparator comparator;

    /** The close handlers. */
    private final Deque> closeHandlers;

    /** The is closed. */
    private boolean isClosed = false;

    /**
     * Instantiates a new exceptional stream.
     *
     * @param iter
     */
    ExceptionalStream(final ExceptionalIterator iter) {
        this(iter, false, null, null);
    }

    /**
     * Instantiates a new exceptional stream.
     *
     * @param iter
     * @param closeHandlers
     */
    ExceptionalStream(final ExceptionalIterator iter, final Deque> closeHandlers) {
        this(iter, false, null, closeHandlers);
    }

    /**
     * Instantiates a new exceptional stream.
     *
     * @param iter
     * @param sorted
     * @param comparator
     * @param closeHandlers
     */
    ExceptionalStream(final ExceptionalIterator iter, final boolean sorted, final Comparator comparator,
            final Deque> closeHandlers) {
        this.elements = iter;
        this.sorted = sorted;
        this.comparator = comparator;
        this.closeHandlers = closeHandlers;
    }

    /**
     *
     * @param 
     * @param 
     * @return
     */
    public static  ExceptionalStream empty() {
        return new ExceptionalStream<>(ExceptionalIterator.EMPTY);
    }

    /**
     *
     * @param 
     * @param 
     * @param e
     * @return
     */
    public static  ExceptionalStream just(final T e) {
        return of(e);
    }

    /**
     *
     * @param 
     * @param 
     * @param e
     * @return
     */
    public static  ExceptionalStream ofNullable(final T e) {
        if (e == null) {
            return empty();
        }

        return of(e);
    }

    /**
     *
     * @param 
     * @param 
     * @param a
     * @return
     */
    public static  ExceptionalStream of(final T... a) {
        if (N.isNullOrEmpty(a)) {
            return empty();
        }

        final int len = N.len(a);

        return newStream(new ExceptionalIterator() {
            private int position = 0;

            @Override
            public boolean hasNext() throws E {
                return position < len;
            }

            @Override
            public T next() throws E {
                if (position >= len) {
                    throw new NoSuchElementException();
                }

                return a[position++];
            }

            @Override
            public long count() throws E {
                return len - position;
            }

            @Override
            public void skip(long n) throws E {
                N.checkArgNotNegative(n, "n");

                if (n > len - position) {
                    position = len;
                }

                position += n;
            }
        });
    }

    /**
     *
     * @param 
     * @param 
     * @param c
     * @return
     */
    public static  ExceptionalStream of(final Collection c) {
        if (N.isNullOrEmpty(c)) {
            return empty();
        }

        return of(c.iterator());
    }

    /**
     *
     * @param 
     * @param 
     * @param iter
     * @return
     */
    public static  ExceptionalStream of(final Iterator iter) {
        if (iter == null) {
            return empty();
        }

        return newStream(ExceptionalIterator. wrap(iter));
    }

    /**
     *
     * @param 
     * @param 
     * @param iterable
     * @return
     */
    public static  ExceptionalStream of(final Iterable iterable) {
        if (iterable == null) {
            return empty();
        }

        return of(iterable.iterator());
    }

    /**
     *
     * @param  the key type
     * @param  the value type
     * @param 
     * @param m
     * @return
     */
    public static  ExceptionalStream, E> of(final Map m) {
        if (m == null) {
            return empty();
        }

        return of(m.entrySet());
    }

    /**
     *
     * @param 
     * @param 
     * @param c
     * @param exceptionType
     * @return
     */
    public static  ExceptionalStream of(final Collection c, final Class exceptionType) {
        return of(c);
    }

    /**
     *
     * @param 
     * @param 
     * @param iter
     * @param exceptionType
     * @return
     */
    public static  ExceptionalStream of(final Iterator iter, final Class exceptionType) {
        return of(iter);
    }

    /**
     *
     * @param 
     * @param 
     * @param iterable
     * @param exceptionType
     * @return
     */
    public static  ExceptionalStream of(final Iterable iterable, final Class exceptionType) {
        return of(iterable);
    }

    /**
     *
     * @param  the key type
     * @param  the value type
     * @param 
     * @param m
     * @param exceptionType
     * @return
     */
    public static  ExceptionalStream, E> of(final Map m, final Class exceptionType) {
        return of(m);
    }

    /**
     *
     * @param 
     * @param a
     * @return
     */
    public static  ExceptionalStream of(final int[] a) {
        if (N.isNullOrEmpty(a)) {
            return empty();
        }

        return newStream(new ExceptionalIterator() {
            private final int len = a.length;
            private int idx = 0;

            @Override
            public boolean hasNext() throws E {
                return idx < len;
            }

            @Override
            public Integer next() throws E {
                return a[idx++];
            }
        });
    }

    /**
     *
     * @param 
     * @param a
     * @return
     */
    public static  ExceptionalStream of(final long[] a) {
        if (N.isNullOrEmpty(a)) {
            return empty();
        }

        return newStream(new ExceptionalIterator() {
            private final int len = a.length;
            private int idx = 0;

            @Override
            public boolean hasNext() throws E {
                return idx < len;
            }

            @Override
            public Long next() throws E {
                return a[idx++];
            }
        });
    }

    /**
     *
     * @param 
     * @param a
     * @return
     */
    public static  ExceptionalStream of(final double[] a) {
        if (N.isNullOrEmpty(a)) {
            return empty();
        }

        return newStream(new ExceptionalIterator() {
            private final int len = a.length;
            private int idx = 0;

            @Override
            public boolean hasNext() throws E {
                return idx < len;
            }

            @Override
            public Double next() throws E {
                return a[idx++];
            }
        });
    }

    public static  ExceptionalStream ofKeys(final Map map) {
        if (N.isNullOrEmpty(map)) {
            return empty();
        }

        return of(map.keySet());
    }

    public static  ExceptionalStream ofValues(final Map map) {
        if (N.isNullOrEmpty(map)) {
            return empty();
        }

        return of(map.values());
    }

    /**
     *
     * @param 
     * @param 
     * @param hasNext
     * @param next
     * @return
     */
    public static  ExceptionalStream iterate(final Throwables.BooleanSupplier hasNext,
            final Throwables.Supplier next) {
        N.checkArgNotNull(hasNext, "hasNext");
        N.checkArgNotNull(next, "next");

        return newStream(new ExceptionalIterator() {
            private boolean hasNextVal = false;

            @Override
            public boolean hasNext() throws E {
                if (hasNextVal == false) {
                    hasNextVal = hasNext.getAsBoolean();
                }

                return hasNextVal;
            }

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

                hasNextVal = false;
                return next.get();
            }
        });
    }

    /**
     *
     * @param 
     * @param 
     * @param init
     * @param hasNext
     * @param f
     * @return
     */
    public static  ExceptionalStream iterate(final T init, final Throwables.BooleanSupplier hasNext,
            final Throwables.UnaryOperator f) {
        N.checkArgNotNull(hasNext, "hasNext");
        N.checkArgNotNull(f, "f");

        return newStream(new ExceptionalIterator() {
            private final T NONE = (T) N.NULL_MASK;
            private T t = NONE;
            private boolean hasNextVal = false;

            @Override
            public boolean hasNext() throws E {
                if (hasNextVal == false) {
                    hasNextVal = hasNext.getAsBoolean();
                }

                return hasNextVal;
            }

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

                hasNextVal = false;
                return t = (t == NONE) ? init : f.apply(t);
            }
        });
    }

    /**
     *
     * @param 
     * @param 
     * @param init
     * @param hasNext
     * @param f
     * @return
     */
    public static  ExceptionalStream iterate(final T init, final Throwables.Predicate hasNext,
            final Throwables.UnaryOperator f) {
        N.checkArgNotNull(hasNext, "hasNext");
        N.checkArgNotNull(f, "f");

        return newStream(new ExceptionalIterator() {
            private final T NONE = (T) N.NULL_MASK;
            private T t = NONE;
            private T cur = NONE;
            private boolean hasMore = true;
            private boolean hasNextVal = false;

            @Override
            public boolean hasNext() throws E {
                if (hasNextVal == false && hasMore) {
                    hasNextVal = hasNext.test((cur = (t == NONE ? init : f.apply(t))));

                    if (hasNextVal == false) {
                        hasMore = false;
                    }
                }

                return hasNextVal;
            }

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

                t = cur;
                cur = NONE;
                hasNextVal = false;
                return t;
            }
        });
    }

    /**
     *
     * @param 
     * @param 
     * @param init
     * @param f
     * @return
     */
    public static  ExceptionalStream iterate(final T init, final Throwables.UnaryOperator f) {
        N.checkArgNotNull(f, "f");

        return newStream(new ExceptionalIterator() {
            private final T NONE = (T) N.NULL_MASK;
            private T t = NONE;

            @Override
            public boolean hasNext() throws E {
                return true;
            }

            @Override
            public T next() throws E {
                return t = t == NONE ? init : f.apply(t);
            }
        });
    }

    public static  ExceptionalStream generate(final Throwables.Supplier supplier) {
        N.checkArgNotNull(supplier, "supplier");

        return newStream(new ExceptionalIterator() {
            @Override
            public boolean hasNext() throws E {
                return true;
            }

            @Override
            public T next() throws E {
                return supplier.get();
            }
        });
    }

    public static  ExceptionalStream repeat(final T element, final long n) {
        N.checkArgNotNegative(n, "n");

        if (n == 0) {
            return empty();
        }

        return newStream(new ExceptionalIterator() {
            private long cnt = n;

            @Override
            public boolean hasNext() throws E {
                return cnt > 0;
            }

            @Override
            public T next() throws E {
                if (cnt-- <= 0) {
                    throw new NoSuchElementException();
                }

                return element;
            }
        });
    }

    /**
     *
     * @param file
     * @return
     */
    public static ExceptionalStream lines(final File file) {
        return lines(file, Charsets.UTF_8);
    }

    /**
     *
     * @param file
     * @param charset
     * @return
     */
    public static ExceptionalStream lines(final File file, final Charset charset) {
        N.checkArgNotNull(file, "file");

        final ExceptionalIterator iter = createLazyLineIterator(file, null, charset, null, true);

        return newStream(iter).onClose(new Throwables.Runnable() {
            @Override
            public void run() throws IOException {
                iter.close();
            }
        });
    }

    /**
     *
     * @param path
     * @return
     */
    public static ExceptionalStream lines(final Path path) {
        return lines(path, Charsets.UTF_8);
    }

    /**
     *
     * @param path
     * @param charset
     * @return
     */
    public static ExceptionalStream lines(final Path path, final Charset charset) {
        N.checkArgNotNull(path, "path");

        final ExceptionalIterator iter = createLazyLineIterator(null, path, charset, null, true);

        return newStream(iter).onClose(new Throwables.Runnable() {
            @Override
            public void run() throws IOException {
                iter.close();
            }
        });
    }

    /**
     *
     * @param reader
     * @return
     */
    public static ExceptionalStream lines(final Reader reader) {
        N.checkArgNotNull(reader, "reader");

        return newStream(createLazyLineIterator(null, null, Charsets.UTF_8, reader, false));
    }

    public static ExceptionalStream listFiles(final File parentPath) {
        if (!parentPath.exists()) {
            return empty();
        }

        return of(parentPath.listFiles());
    }

    public static ExceptionalStream listFiles(final File parentPath, final boolean recursively) {
        if (!parentPath.exists()) {
            return empty();
        } else if (recursively == false) {
            return of(parentPath.listFiles());
        }

        final ExceptionalIterator iter = new ExceptionalIterator() {
            private final Queue paths = N.asLinkedList(parentPath);
            private File[] subFiles = null;
            private int cursor = 0;

            @Override
            public boolean hasNext() {
                if ((subFiles == null || cursor >= subFiles.length) && paths.size() > 0) {
                    cursor = 0;
                    subFiles = null;

                    while (paths.size() > 0) {
                        subFiles = paths.poll().listFiles();

                        if (N.notNullOrEmpty(subFiles)) {
                            break;
                        }
                    }
                }

                return subFiles != null && cursor < subFiles.length;
            }

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

                if (subFiles[cursor].isDirectory()) {
                    paths.offer(subFiles[cursor]);
                }

                return subFiles[cursor++];
            }
        };

        return newStream(iter);
    }

    /**
     * Creates the lazy line iterator.
     *
     * @param file
     * @param path
     * @param charset
     * @param reader
     * @param closeReader
     * @return
     */
    private static ExceptionalIterator createLazyLineIterator(final File file, final Path path, final Charset charset, final Reader reader,
            final boolean closeReader) {
        return ExceptionalIterator.of(new Throwables.Supplier, IOException>() {
            private ExceptionalIterator lazyIter = null;

            @Override
            public synchronized ExceptionalIterator get() {
                if (lazyIter == null) {
                    lazyIter = new ExceptionalIterator() {
                        private BufferedReader bufferedReader;

                        {
                            if (reader != null) {
                                bufferedReader = reader instanceof BufferedReader ? ((BufferedReader) reader) : new BufferedReader(reader);
                            } else if (file != null) {
                                bufferedReader = IOUtil.newBufferedReader(file, charset == null ? Charsets.UTF_8 : charset);
                            } else {
                                bufferedReader = IOUtil.newBufferedReader(path, charset == null ? Charsets.UTF_8 : charset);
                            }
                        }

                        private String cachedLine;
                        private boolean finished = false;

                        @Override
                        public boolean hasNext() throws IOException {
                            if (this.cachedLine != null) {
                                return true;
                            } else if (this.finished) {
                                return false;
                            } else {
                                this.cachedLine = this.bufferedReader.readLine();
                                if (this.cachedLine == null) {
                                    this.finished = true;
                                    return false;
                                } else {
                                    return true;
                                }
                            }
                        }

                        @Override
                        public String next() throws IOException {
                            if (!this.hasNext()) {
                                throw new NoSuchElementException("No more lines");
                            } else {
                                String res = this.cachedLine;
                                this.cachedLine = null;
                                return res;
                            }
                        }

                        @Override
                        public void close() throws IOException {
                            if (closeReader) {
                                IOUtil.close(bufferedReader);
                            }
                        }
                    };
                }

                return lazyIter;
            }
        });
    }

    @SafeVarargs
    public static  ExceptionalStream concat(final T[]... a) {
        if (N.isNullOrEmpty(a)) {
            return empty();
        }

        return of(Iterators.concat(a));
    }

    @SafeVarargs
    public static  ExceptionalStream concat(final Collection... a) {
        if (N.isNullOrEmpty(a)) {
            return empty();
        }

        return of(Iterators.concat(a));
    }

    /**
     *
     * @param 
     * @param 
     * @param a
     * @return
     */
    @SafeVarargs
    public static  ExceptionalStream concat(final ExceptionalStream... a) {
        if (N.isNullOrEmpty(a)) {
            return empty();
        }

        return concat(Array.asList(a));
    }

    /**
     *
     * @param 
     * @param 
     * @param c
     * @return
     */
    public static  ExceptionalStream concat(final Collection> c) {
        if (N.isNullOrEmpty(c)) {
            return empty();
        }

        return newStream(new ExceptionalIterator() {
            private final Iterator> iterators = c.iterator();
            private ExceptionalStream cur;
            private ExceptionalIterator iter;

            @Override
            public boolean hasNext() throws E {
                while ((iter == null || iter.hasNext() == false) && iterators.hasNext()) {
                    if (cur != null) {
                        cur.close();
                    }

                    cur = iterators.next();
                    iter = cur.elements;
                }

                return iter != null && iter.hasNext();
            }

            @Override
            public T next() throws E {
                if ((iter == null || iter.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return iter.next();
            }
        }, mergeCloseHandlers(c));
    }

    /**
     * Zip together the "a" and "b" arrays until one of them runs out of values.
     * Each pair of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @return
     */
    public static  ExceptionalStream zip(final A[] a, final B[] b,
            final Throwables.BiFunction zipFunction) {
        return zip(ObjIterator.of(a), ObjIterator.of(b), zipFunction);
    }

    /**
     * Zip together the "a", "b" and "c" arrays until one of them runs out of values.
     * Each triple of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @return
     */
    public static  ExceptionalStream zip(final A[] a, final B[] b, final C[] c,
            final Throwables.TriFunction zipFunction) {
        return zip(ObjIterator.of(a), ObjIterator.of(b), ObjIterator.of(c), zipFunction);
    }

    /**
     * Zip together the "a" and "b" arrays until one of them runs out of values.
     * Each pair of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @return
     */
    public static  ExceptionalStream zip(final Collection a, final Collection b,
            final Throwables.BiFunction zipFunction) {
        return zip(Iterables.iterator(a), Iterables.iterator(b), zipFunction);
    }

    /**
     * Zip together the "a", "b" and "c" arrays until one of them runs out of values.
     * Each triple of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @return
     */
    public static  ExceptionalStream zip(final Collection a, final Collection b,
            final Collection c, final Throwables.TriFunction zipFunction) {
        return zip(Iterables.iterator(a), Iterables.iterator(b), Iterables.iterator(c), zipFunction);
    }

    /**
     * Zip together the "a" and "b" iterators until one of them runs out of values.
     * Each pair of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @return
     */
    public static  ExceptionalStream zip(final Iterator a, final Iterator b,
            final Throwables.BiFunction zipFunction) {
        return newStream(new ExceptionalIterator() {
            @Override
            public boolean hasNext() throws E {
                return a.hasNext() && b.hasNext();
            }

            @Override
            public T next() throws E {
                return zipFunction.apply(a.next(), b.next());
            }
        });
    }

    /**
     * Zip together the "a", "b" and "c" iterators until one of them runs out of values.
     * Each triple of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @return
     */
    public static  ExceptionalStream zip(final Iterator a, final Iterator b,
            final Iterator c, final Throwables.TriFunction zipFunction) {
        return newStream(new ExceptionalIterator() {
            @Override
            public boolean hasNext() throws E {
                return a.hasNext() && b.hasNext() && c.hasNext();
            }

            @Override
            public T next() throws E {
                return zipFunction.apply(a.next(), b.next(), c.next());
            }
        });
    }

    /**
     * Zip together the "a" and "b" streams until one of them runs out of values.
     * Each pair of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @return
     */
    public static  ExceptionalStream zip(final ExceptionalStream a,
            final ExceptionalStream b, final Throwables.BiFunction zipFunction) {
        return newStream(new ExceptionalIterator() {
            private final ExceptionalIterator iterA = a.elements;
            private final ExceptionalIterator iterB = b.elements;

            @Override
            public boolean hasNext() throws E {
                return iterA.hasNext() && iterB.hasNext();
            }

            @Override
            public T next() throws E {
                return zipFunction.apply(iterA.next(), iterB.next());
            }
        }, mergeCloseHandlers(Array.asList(a, b)));
    }

    /**
     * Zip together the "a", "b" and "c" streams until one of them runs out of values.
     * Each triple of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @param c
     * @return
     */
    public static  ExceptionalStream zip(final ExceptionalStream a,
            final ExceptionalStream b, final ExceptionalStream c,
            final Throwables.TriFunction zipFunction) {
        return newStream(new ExceptionalIterator() {
            private final ExceptionalIterator iterA = a.elements;
            private final ExceptionalIterator iterB = b.elements;
            private final ExceptionalIterator iterC = c.elements;

            @Override
            public boolean hasNext() throws E {
                return iterA.hasNext() && iterB.hasNext() && iterC.hasNext();
            }

            @Override
            public T next() throws E {
                return zipFunction.apply(iterA.next(), iterB.next(), iterC.next());
            }
        }, mergeCloseHandlers(Array.asList(a, b, c)));
    }

    /**
     * Zip together the "a" and "b" iterators until all of them runs out of values.
     * Each pair of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @param valueForNoneA value to fill if "a" runs out of values first.
     * @param valueForNoneB value to fill if "b" runs out of values first.
     * @param zipFunction
     * @return
     */
    public static  ExceptionalStream zip(final A[] a, final B[] b, final A valueForNoneA, final B valueForNoneB,
            final Throwables.BiFunction zipFunction) {
        return zip(ObjIterator.of(a), ObjIterator.of(b), valueForNoneA, valueForNoneB, zipFunction);
    }

    /**
     * Zip together the "a", "b" and "c" iterators until all of them runs out of values.
     * Each triple of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @param c
     * @param valueForNoneA value to fill if "a" runs out of values.
     * @param valueForNoneB value to fill if "b" runs out of values.
     * @param valueForNoneC value to fill if "c" runs out of values.
     * @param zipFunction
     * @return
     */
    public static  ExceptionalStream zip(final A[] a, final B[] b, final C[] c, final A valueForNoneA,
            final B valueForNoneB, final C valueForNoneC, final Throwables.TriFunction zipFunction) {
        return zip(ObjIterator.of(a), ObjIterator.of(b), ObjIterator.of(c), valueForNoneA, valueForNoneB, valueForNoneC, zipFunction);
    }

    /**
     * Zip together the "a" and "b" iterators until all of them runs out of values.
     * Each pair of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @param valueForNoneA value to fill if "a" runs out of values first.
     * @param valueForNoneB value to fill if "b" runs out of values first.
     * @param zipFunction
     * @return
     */
    public static  ExceptionalStream zip(final Collection a, final Collection b,
            final A valueForNoneA, final B valueForNoneB, final Throwables.BiFunction zipFunction) {
        return zip(Iterables.iterator(a), Iterables.iterator(b), valueForNoneA, valueForNoneB, zipFunction);
    }

    /**
     * Zip together the "a", "b" and "c" iterators until all of them runs out of values.
     * Each triple of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @param c
     * @param valueForNoneA value to fill if "a" runs out of values.
     * @param valueForNoneB value to fill if "b" runs out of values.
     * @param valueForNoneC value to fill if "c" runs out of values.
     * @param zipFunction
     * @return
     */
    public static  ExceptionalStream zip(final Collection a, final Collection b,
            final Collection c, final A valueForNoneA, final B valueForNoneB, final C valueForNoneC,
            final Throwables.TriFunction zipFunction) {
        return zip(Iterables.iterator(a), Iterables.iterator(b), Iterables.iterator(c), valueForNoneA, valueForNoneB, valueForNoneC, zipFunction);
    }

    /**
     * Zip together the "a" and "b" iterators until all of them runs out of values.
     * Each pair of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @param valueForNoneA value to fill if "a" runs out of values first.
     * @param valueForNoneB value to fill if "b" runs out of values first.
     * @param zipFunction
     * @return
     */
    public static  ExceptionalStream zip(final Iterator a, final Iterator b,
            final A valueForNoneA, final B valueForNoneB, final Throwables.BiFunction zipFunction) {
        return newStream(new ExceptionalIterator() {
            @Override
            public boolean hasNext() throws E {
                return a.hasNext() || b.hasNext();
            }

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

                return zipFunction.apply(a.hasNext() ? a.next() : valueForNoneA, b.hasNext() ? b.next() : valueForNoneB);
            }
        });
    }

    /**
     * Zip together the "a", "b" and "c" iterators until all of them runs out of values.
     * Each triple of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @param c
     * @param valueForNoneA value to fill if "a" runs out of values.
     * @param valueForNoneB value to fill if "b" runs out of values.
     * @param valueForNoneC value to fill if "c" runs out of values.
     * @param zipFunction
     * @return
     */
    public static  ExceptionalStream zip(final Iterator a, final Iterator b,
            final Iterator c, final A valueForNoneA, final B valueForNoneB, final C valueForNoneC,
            final Throwables.TriFunction zipFunction) {
        return newStream(new ExceptionalIterator() {
            @Override
            public boolean hasNext() throws E {
                return a.hasNext() || b.hasNext() || c.hasNext();
            }

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

                return zipFunction.apply(a.hasNext() ? a.next() : valueForNoneA, b.hasNext() ? b.next() : valueForNoneB,
                        c.hasNext() ? c.next() : valueForNoneC);
            }
        });
    }

    /**
     * Zip together the "a" and "b" streams until one of them runs out of values.
     * Each pair of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @return
     */
    public static  ExceptionalStream zip(final ExceptionalStream a,
            final ExceptionalStream b, final A valueForNoneA, final B valueForNoneB,
            final Throwables.BiFunction zipFunction) {
        return newStream(new ExceptionalIterator() {
            private final ExceptionalIterator iterA = a.elements;
            private final ExceptionalIterator iterB = b.elements;

            @Override
            public boolean hasNext() throws E {
                return iterA.hasNext() || iterB.hasNext();
            }

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

                return zipFunction.apply(iterA.hasNext() ? iterA.next() : valueForNoneA, iterB.hasNext() ? iterB.next() : valueForNoneB);
            }
        }, mergeCloseHandlers(Array.asList(a, b)));
    }

    /**
     * Zip together the "a", "b" and "c" streams until one of them runs out of values.
     * Each triple of values is combined into a single value using the supplied zipFunction function.
     *
     * @param a
     * @param b
     * @param c
     * @return
     */
    public static  ExceptionalStream zip(final ExceptionalStream a,
            final ExceptionalStream b, final ExceptionalStream c, final A valueForNoneA, final B valueForNoneB,
            final C valueForNoneC, final Throwables.TriFunction zipFunction) {
        return newStream(new ExceptionalIterator() {
            private final ExceptionalIterator iterA = a.elements;
            private final ExceptionalIterator iterB = b.elements;
            private final ExceptionalIterator iterC = c.elements;

            @Override
            public boolean hasNext() throws E {
                return iterA.hasNext() || iterB.hasNext() || iterC.hasNext();
            }

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

                return zipFunction.apply(iterA.hasNext() ? iterA.next() : valueForNoneA, iterB.hasNext() ? iterB.next() : valueForNoneB,
                        iterC.hasNext() ? iterC.next() : valueForNoneC);
            }
        }, mergeCloseHandlers(Array.asList(a, b, c)));
    }

    private static  Deque> mergeCloseHandlers(
            Collection> closeHandlersList) {
        if (N.isNullOrEmpty(closeHandlersList)) {
            return null;
        }

        int count = 0;

        for (ExceptionalStream s : closeHandlersList) {
            count += N.size(s.closeHandlers);
        }

        if (count == 0) {
            return null;
        }

        final Deque> newCloseHandlers = new ArrayDeque<>(count);

        for (ExceptionalStream s : closeHandlersList) {
            if (N.notNullOrEmpty(s.closeHandlers)) {
                newCloseHandlers.addAll(s.closeHandlers);
            }
        }

        return newCloseHandlers;
    }

    /**
     *
     * @param predicate
     * @return
     */
    public ExceptionalStream filter(final Throwables.Predicate predicate) {
        checkArgNotNull(predicate, "predicate");

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

            @Override
            public boolean hasNext() throws E {
                if (hasNext == false) {
                    while (elements.hasNext()) {
                        next = elements.next();

                        if (predicate.test(next)) {
                            hasNext = true;
                            break;
                        }
                    }
                }

                return hasNext;
            }

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

                hasNext = false;

                return next;
            }
        }, sorted, comparator, closeHandlers);
    }

    /**
     *
     * @param predicate
     * @return
     */
    public ExceptionalStream takeWhile(final Throwables.Predicate predicate) {
        checkArgNotNull(predicate, "predicate");

        return newStream(new ExceptionalIterator() {
            private boolean hasMore = true;
            private boolean hasNext = false;
            private T next = null;

            @Override
            public boolean hasNext() throws E {
                if (hasNext == false && hasMore && elements.hasNext()) {
                    next = elements.next();

                    if (predicate.test(next)) {
                        hasNext = true;
                    } else {
                        hasMore = false;
                    }
                }

                return hasNext;
            }

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

                hasNext = false;

                return next;
            }
        }, sorted, comparator, closeHandlers);
    }

    /**
     *
     * @param predicate
     * @return
     */
    public ExceptionalStream dropWhile(final Throwables.Predicate predicate) {
        checkArgNotNull(predicate, "predicate");

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

            @Override
            public boolean hasNext() throws E {
                if (hasNext == false) {
                    if (dropped == false) {
                        dropped = true;

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

                            if (predicate.test(next) == false) {
                                hasNext = true;
                                break;
                            }
                        }
                    } else if (elements.hasNext()) {
                        next = elements.next();
                        hasNext = true;
                    }
                }

                return hasNext;
            }

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

                hasNext = false;

                return next;
            }

        }, sorted, comparator, closeHandlers);
    }

    /**
     * Distinct and filter by occurrences.
     *
     * @return
     */
    public ExceptionalStream distinct() {
        final Set set = N.newHashSet();

        return filter(new Throwables.Predicate() {
            @Override
            public boolean test(T value) {
                return set.add(hashKey(value));
            }
        });
    }

    /**
     * Distinct by the value mapped from keyMapper .
     *
     * @param keyMapper don't change value of the input parameter.
     * @return
     */
    public ExceptionalStream distinctBy(final Throwables.Function keyMapper) {
        checkArgNotNull(keyMapper, "keyMapper");

        final Set set = N.newHashSet();

        return filter(new Throwables.Predicate() {
            @Override
            public boolean test(T value) throws E {
                return set.add(hashKey(keyMapper.apply(value)));
            }
        });
    }

    /**
     * Distinct and filter by occurrences.
     *
     * @param keyMapper
     * @param occurrencesFilter
     * @return
     */
    @SuppressWarnings("rawtypes")
    public ExceptionalStream distinctBy(final Throwables.Function keyMapper,
            final Throwables.Predicate occurrencesFilter) {
        final Supplier, Long>> supplier = Suppliers., Long> ofLinkedHashMap();

        final Throwables.Function, E> keyedMapper = new Throwables.Function, E>() {
            @Override
            public Keyed apply(T t) throws E {
                return Keyed.of((Object) keyMapper.apply(t), t);
            }
        };

        final Throwables.Predicate, Long>, E> predicate = new Throwables.Predicate, Long>, E>() {
            @Override
            public boolean test(Map.Entry, Long> e) throws E {
                return occurrencesFilter.test(e.getValue());
            }
        };

        return groupBy(keyedMapper, Collectors.counting(), supplier).filter(predicate)
                .map((Throwables.Function, Long>, T, E>) (Throwables.Function) KK);
    }

    /**
     *
     * @param 
     * @param mapper
     * @return
     */
    public  ExceptionalStream map(final Throwables.Function mapper) {
        checkArgNotNull(mapper, "mapper");

        return newStream(new ExceptionalIterator() {
            @Override
            public boolean hasNext() throws E {
                return elements.hasNext();
            }

            @Override
            public U next() throws E {
                return mapper.apply(elements.next());
            }
        }, closeHandlers);
    }

    /**
     *
     * @param 
     * @param mapper
     * @return
     */
    public  ExceptionalStream flatMap(
            final Throwables.Function, ? extends E> mapper) {
        checkArgNotNull(mapper, "mapper");

        final ExceptionalIterator iter = new ExceptionalIterator() {
            private ExceptionalIterator cur = null;
            private ExceptionalStream s = null;
            private Throwables.Runnable closeHandle = null;

            @Override
            public boolean hasNext() throws E {
                while ((cur == null || cur.hasNext() == false) && elements.hasNext()) {
                    if (closeHandle != null) {
                        final Throwables.Runnable tmp = closeHandle;
                        closeHandle = null;
                        tmp.run();
                    }

                    s = mapper.apply(elements.next());

                    if (N.notNullOrEmpty(s.closeHandlers)) {
                        closeHandle = new Throwables.Runnable() {
                            @Override
                            public void run() throws E {
                                s.close();
                            }
                        };
                    }

                    cur = s.elements;
                }

                return cur != null && cur.hasNext();
            }

            @Override
            public R next() throws E {
                if ((cur == null || cur.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur.next();
            }

            @Override
            public void close() throws E {
                if (closeHandle != null) {
                    final Throwables.Runnable tmp = closeHandle;
                    closeHandle = null;
                    tmp.run();
                }
            }
        };

        final Deque> newCloseHandlers = new ArrayDeque<>(N.size(closeHandlers) + 1);

        newCloseHandlers.add(new Throwables.Runnable() {
            @Override
            public void run() throws E {
                iter.close();
            }
        });

        if (N.notNullOrEmpty(closeHandlers)) {
            newCloseHandlers.addAll(closeHandlers);
        }

        return newStream(iter, newCloseHandlers);
    }

    /**
     *
     * @param 
     * @param mapper
     * @return
     */
    public  ExceptionalStream flattMap(final Throwables.Function, ? extends E> mapper) {
        checkArgNotNull(mapper, "mapper");

        return newStream(new ExceptionalIterator() {
            private Iterator cur = null;
            private Collection c = null;

            @Override
            public boolean hasNext() throws E {
                while ((cur == null || cur.hasNext() == false) && elements.hasNext()) {
                    c = mapper.apply(elements.next());
                    cur = N.isNullOrEmpty(c) ? null : c.iterator();
                }

                return cur != null && cur.hasNext();
            }

            @Override
            public R next() throws E {
                if ((cur == null || cur.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur.next();
            }
        }, closeHandlers);
    }

    /**
     *
     * @param 
     * @param mapper
     * @return
     */
    public  ExceptionalStream slidingMap(Throwables.BiFunction mapper) {
        return slidingMap(mapper, 1);
    }

    /**
     *
     * @param 
     * @param mapper
     * @param increment
     * @return
     */
    public  ExceptionalStream slidingMap(Throwables.BiFunction mapper, int increment) {
        return slidingMap(mapper, increment, false);
    }

    /**
     *
     * @param 
     * @param mapper
     * @param increment
     * @param ignoreNotPaired
     * @return
     */
    public  ExceptionalStream slidingMap(final Throwables.BiFunction mapper, final int increment,
            final boolean ignoreNotPaired) {
        final int windowSize = 2;

        checkArgPositive(increment, "increment");

        return newStream(new ExceptionalIterator() {
            @SuppressWarnings("unchecked")
            private final T NONE = (T) N.NULL_MASK;
            private T prev = NONE;
            private T _1 = NONE;

            @Override
            public boolean hasNext() throws E {
                if (increment > windowSize && prev != NONE) {
                    int skipNum = increment - windowSize;

                    while (skipNum-- > 0 && elements.hasNext()) {
                        elements.next();
                    }

                    prev = NONE;
                }

                if (ignoreNotPaired && _1 == NONE && elements.hasNext()) {
                    _1 = elements.next();
                }

                return elements.hasNext();
            }

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

                if (ignoreNotPaired) {
                    final R res = mapper.apply(_1, (prev = elements.next()));
                    _1 = increment == 1 ? prev : NONE;
                    return res;
                } else {
                    if (increment == 1) {
                        return mapper.apply(prev == NONE ? elements.next() : prev, (prev = (elements.hasNext() ? elements.next() : null)));
                    } else {
                        return mapper.apply(elements.next(), (prev = (elements.hasNext() ? elements.next() : null)));
                    }
                }
            }
        }, closeHandlers);
    }

    /**
     *
     * @param 
     * @param mapper
     * @return
     */
    public  ExceptionalStream slidingMap(Throwables.TriFunction mapper) {
        return slidingMap(mapper, 1);
    }

    /**
     *
     * @param 
     * @param mapper
     * @param increment
     * @return
     */
    public  ExceptionalStream slidingMap(Throwables.TriFunction mapper, int increment) {
        return slidingMap(mapper, increment, false);
    }

    /**
     *
     * @param 
     * @param mapper
     * @param increment
     * @param ignoreNotPaired
     * @return
     */
    public  ExceptionalStream slidingMap(final Throwables.TriFunction mapper, final int increment,
            final boolean ignoreNotPaired) {
        final int windowSize = 3;

        checkArgPositive(increment, "increment");

        return newStream(new ExceptionalIterator() {
            @SuppressWarnings("unchecked")
            private final T NONE = (T) N.NULL_MASK;
            private T prev = NONE;
            private T prev2 = NONE;
            private T _1 = NONE;
            private T _2 = NONE;

            @Override
            public boolean hasNext() throws E {
                if (increment > windowSize && prev != NONE) {
                    int skipNum = increment - windowSize;

                    while (skipNum-- > 0 && elements.hasNext()) {
                        elements.next();
                    }

                    prev = NONE;
                }

                if (ignoreNotPaired) {
                    if (_1 == NONE && elements.hasNext()) {
                        _1 = elements.next();
                    }

                    if (_2 == NONE && elements.hasNext()) {
                        _2 = elements.next();
                    }
                }

                return elements.hasNext();
            }

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

                if (ignoreNotPaired) {
                    final R res = mapper.apply(_1, _2, (prev = elements.next()));
                    _1 = increment == 1 ? _2 : (increment == 2 ? prev : NONE);
                    _2 = increment == 1 ? prev : NONE;
                    return res;
                } else {
                    if (increment == 1) {
                        return mapper.apply(prev2 == NONE ? elements.next() : prev2,
                                (prev2 = (prev == NONE ? (elements.hasNext() ? elements.next() : null) : prev)),
                                (prev = (elements.hasNext() ? elements.next() : null)));

                    } else if (increment == 2) {
                        return mapper.apply(prev == NONE ? elements.next() : prev, elements.hasNext() ? elements.next() : null,
                                (prev = (elements.hasNext() ? elements.next() : null)));
                    } else {
                        return mapper.apply(elements.next(), elements.hasNext() ? elements.next() : null,
                                (prev = (elements.hasNext() ? elements.next() : null)));
                    }
                }
            }
        }, closeHandlers);
    }

    /**
     *
     * @param  the key type
     * @param keyMapper
     * @return
     */
    public  ExceptionalStream>, E> groupBy(final Throwables.Function keyMapper) {
        return groupBy(keyMapper, Suppliers.> ofMap());
    }

    /**
     *
     * @param  the key type
     * @param keyMapper
     * @param mapFactory
     * @return
     */
    public  ExceptionalStream>, E> groupBy(final Throwables.Function keyMapper,
            final Supplier>> mapFactory) {
        return groupBy(keyMapper, Fnn. identity(), mapFactory);
    }

    /**
     *
     * @param  the key type
     * @param  the value type
     * @param keyMapper
     * @param valueMapper
     * @return
     * @see Collectors#toMultimap(Function, Function)
     */
    public  ExceptionalStream>, E> groupBy(Throwables.Function keyMapper,
            Throwables.Function valueMapper) {
        return groupBy(keyMapper, valueMapper, Suppliers.> ofMap());
    }

    /**
     *
     * @param  the key type
     * @param  the value type
     * @param keyMapper
     * @param valueMapper
     * @param mapFactory
     * @return
     * @see Collectors#toMultimap(Function, Function, Supplier)
     */
    public  ExceptionalStream>, E> groupBy(final Throwables.Function keyMapper,
            final Throwables.Function valueMapper, final Supplier>> mapFactory) {
        checkArgNotNull(keyMapper, "keyMapper");
        checkArgNotNull(valueMapper, "valueMapper");
        checkArgNotNull(mapFactory, "mapFactory");

        return newStream(new ExceptionalIterator>, E>() {
            private Iterator>> iter = null;

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

            @Override
            public Map.Entry> next() throws E {
                init();
                return iter.next();
            }

            private void init() throws E {
                if (iter == null) {
                    iter = ExceptionalStream.this.groupTo(keyMapper, valueMapper, mapFactory).entrySet().iterator();
                }
            }
        }, closeHandlers);

    }

    /**
     *
     * @param  the key type
     * @param  the value type
     * @param keyMapper
     * @param valueMapper
     * @param mergeFunction
     * @return
     */
    public  ExceptionalStream, E> groupBy(final Throwables.Function keyMapper,
            final Throwables.Function valueMapper, Throwables.BinaryOperator mergeFunction) {
        return groupBy(keyMapper, valueMapper, mergeFunction, Suppliers. ofMap());
    }

    /**
     *
     * @param  the key type
     * @param  the value type
     * @param keyMapper
     * @param valueMapper
     * @param mergeFunction
     * @param mapFactory
     * @return
     * @see {@link Fn.Fnn#throwingMerger()}
     * @see {@link Fn.Fnn#replacingMerger()}
     * @see {@link Fn.Fnn#ignoringMerger()}
     */
    public  ExceptionalStream, E> groupBy(final Throwables.Function keyMapper,
            final Throwables.Function valueMapper, final Throwables.BinaryOperator mergeFunction,
            final Supplier> mapFactory) {
        checkArgNotNull(keyMapper, "keyMapper");
        checkArgNotNull(valueMapper, "valueMapper");
        checkArgNotNull(mergeFunction, "mergeFunction");
        checkArgNotNull(mapFactory, "mapFactory");

        return newStream(new ExceptionalIterator, E>() {
            private Iterator> iter = null;

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

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

            private void init() throws E {
                if (iter == null) {
                    iter = ExceptionalStream.this.toMap(keyMapper, valueMapper, mergeFunction, mapFactory).entrySet().iterator();
                }
            }
        }, closeHandlers);
    }

    /**
     *
     * @param  the key type
     * @param 
     * @param 
     * @param keyMapper
     * @param downstream
     * @return
     * @throws E the e
     */
    public  ExceptionalStream, E> groupBy(final Throwables.Function keyMapper,
            final Collector downstream) {
        return groupBy(keyMapper, downstream, Suppliers. ofMap());
    }

    /**
     *
     * @param  the key type
     * @param 
     * @param 
     * @param keyMapper
     * @param downstream
     * @param mapFactory
     * @return
     * @throws E the e
     */
    public  ExceptionalStream, E> groupBy(final Throwables.Function keyMapper,
            final Collector downstream, final Supplier> mapFactory) {
        return groupBy(keyMapper, Fnn. identity(), downstream, mapFactory);
    }

    /**
     *
     * @param  the key type
     * @param  the value type
     * @param 
     * @param 
     * @param keyMapper
     * @param valueMapper
     * @param downstream
     * @return
     * @throws E the e
     */
    public  ExceptionalStream, E> groupBy(final Throwables.Function keyMapper,
            final Throwables.Function valueMapper, final Collector downstream) {
        return groupBy(keyMapper, valueMapper, downstream, Suppliers. ofMap());
    }

    /**
     *
     * @param  the key type
     * @param  the value type
     * @param 
     * @param 
     * @param keyMapper
     * @param valueMapper
     * @param downstream
     * @param mapFactory
     * @return
     * @throws E the e
     */
    public  ExceptionalStream, E> groupBy(final Throwables.Function keyMapper,
            final Throwables.Function valueMapper, final Collector downstream,
            final Supplier> mapFactory) {
        checkArgNotNull(keyMapper, "keyMapper");
        checkArgNotNull(valueMapper, "valueMapper");
        checkArgNotNull(downstream, "downstream");
        checkArgNotNull(mapFactory, "mapFactory");

        return newStream(new ExceptionalIterator, E>() {
            private Iterator> iter = null;

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

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

            private void init() throws E {
                if (iter == null) {
                    iter = ExceptionalStream.this.toMap(keyMapper, valueMapper, downstream, mapFactory).entrySet().iterator();
                }
            }
        }, closeHandlers);
    }

    /**
     *
     * @param 
     * @param keyMapper
     * @return
     */
    public  ExceptionalStream, E> countBy(final Throwables.Function keyMapper) {
        return groupBy(keyMapper, Collectors.countingInt());
    }

    /**
     *
     * @param 
     * @param collapsible
     * @param supplier
     * @return
     */
    public > ExceptionalStream collapse(final Throwables.BiPredicate collapsible,
            final Supplier supplier) {
        checkArgNotNull(collapsible, "collapsible");
        checkArgNotNull(supplier, "supplier");

        final ExceptionalIterator iter = elements;

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

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

            @Override
            public C next() throws E {
                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;
            }
        }, closeHandlers);
    }

    /**
     * Merge series of adjacent elements which satisfy the given predicate using
     * the merger function and return a new stream.
     *
     * 

Example: *

     * 
     * Stream.of(new Integer[0]).collapse((a, b) -> a < b, (a, b) -> a + b) => []
     * Stream.of(1).collapse((a, b) -> a < b, (a, b) -> a + b) => [1]
     * Stream.of(1, 2).collapse((a, b) -> a < b, (a, b) -> a + b) => [3]
     * Stream.of(1, 2, 3).collapse((a, b) -> a < b, (a, b) -> a + b) => [6]
     * Stream.of(1, 2, 3, 3, 2, 1).collapse((a, b) -> a < b, (a, b) -> a + b) => [6, 3, 2, 1]
     * 
     * 
* *
* This method only run sequentially, even in parallel stream. * * @param collapsible * @param mergeFunction * @return */ public ExceptionalStream collapse(final Throwables.BiPredicate collapsible, final Throwables.BiFunction mergeFunction) { checkArgNotNull(collapsible, "collapsible"); checkArgNotNull(mergeFunction, "mergeFunction"); final ExceptionalIterator iter = elements; return newStream(new ExceptionalIterator() { private boolean hasNext = false; private T next = null; @Override public boolean hasNext() throws E { return hasNext || iter.hasNext(); } @Override public T next() throws E { 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; } }, closeHandlers); } /** * * @param * @param collapsible * @param init * @param op * @return */ public ExceptionalStream collapse(final Throwables.BiPredicate collapsible, final U init, final Throwables.BiFunction op) { checkArgNotNull(collapsible, "collapsible"); checkArgNotNull(op, "accumulator"); final ExceptionalIterator iter = elements; return newStream(new ExceptionalIterator() { private boolean hasNext = false; private T next = null; @Override public boolean hasNext() throws E { return hasNext || iter.hasNext(); } @Override public U next() throws E { 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; } }, closeHandlers); } /** * * @param * @param collapsible * @param supplier * @param accumulator * @return */ public ExceptionalStream collapse(final Throwables.BiPredicate collapsible, final Throwables.Supplier supplier, final Throwables.BiConsumer accumulator) { checkArgNotNull(collapsible, "collapsible"); checkArgNotNull(supplier, "supplier"); checkArgNotNull(accumulator, "accumulator"); final ExceptionalIterator iter = elements; return newStream(new ExceptionalIterator() { private boolean hasNext = false; private T next = null; @Override public boolean hasNext() throws E { return hasNext || iter.hasNext(); } @Override public R next() throws E { final R container = supplier.get(); accumulator.accept(container, hasNext ? next : (next = iter.next())); while ((hasNext = iter.hasNext())) { if (collapsible.test(next, (next = iter.next()))) { accumulator.accept(container, next); } else { break; } } return container; } }, closeHandlers); } /** * * @param * @param
* @param collapsible * @param collector * @return */ public ExceptionalStream collapse(final Throwables.BiPredicate collapsible, final Collector collector) { checkArgNotNull(collapsible, "collapsible"); checkArgNotNull(collector, "collector"); final Supplier supplier = collector.supplier(); final BiConsumer accumulator = collector.accumulator(); final Function finisher = collector.finisher(); final ExceptionalIterator iter = elements; return newStream(new ExceptionalIterator() { private boolean hasNext = false; private T next = null; @Override public boolean hasNext() throws E { return hasNext || iter.hasNext(); } @Override public R next() throws E { final A container = supplier.get(); accumulator.accept(container, hasNext ? next : (next = iter.next())); while ((hasNext = iter.hasNext())) { if (collapsible.test(next, (next = iter.next()))) { accumulator.accept(container, next); } else { break; } } return finisher.apply(container); } }, closeHandlers); } /** * * @param accumulator * @return */ public ExceptionalStream scan(final Throwables.BiFunction accumulator) { final ExceptionalIterator iter = elements; return newStream(new ExceptionalIterator() { private T res = null; private boolean isFirst = true; @Override public boolean hasNext() throws E { return iter.hasNext(); } @Override public T next() throws E { if (isFirst) { isFirst = false; return (res = iter.next()); } else { return (res = accumulator.apply(res, iter.next())); } } }, closeHandlers); } /** * * @param * @param init * @param accumulator * @return */ public ExceptionalStream scan(final U init, final Throwables.BiFunction accumulator) { final ExceptionalIterator iter = elements; return newStream(new ExceptionalIterator() { private U res = init; @Override public boolean hasNext() throws E { return iter.hasNext(); } @Override public U next() throws E { return (res = accumulator.apply(res, iter.next())); } }, closeHandlers); } /** * * @param * @param init * @param accumulator * @param initIncluded * @return */ public ExceptionalStream scan(final U init, final Throwables.BiFunction accumulator, final boolean initIncluded) { if (initIncluded == false) { return scan(init, accumulator); } final ExceptionalIterator iter = elements; return newStream(new ExceptionalIterator() { private boolean isFirst = true; private U res = init; @Override public boolean hasNext() throws E { return isFirst || iter.hasNext(); } @Override public U next() throws E { if (isFirst) { isFirst = false; return init; } return (res = accumulator.apply(res, iter.next())); } }, closeHandlers); } /** * * @param defaultValue * @return * @see #appendIfEmpty(Object...) */ @SequentialOnly @IntermediateOp public final ExceptionalStream defaultIfEmpty(final T defaultValue) { return appendIfEmpty(defaultValue); } /** * * @param supplier * @return * @see #appendIfEmpty(Supplier) */ @SequentialOnly @IntermediateOp public final ExceptionalStream defaultIfEmpty(final Supplier> supplier) { return appendIfEmpty(supplier); } @SafeVarargs public final ExceptionalStream prepend(final T... a) { return prepend(ExceptionalStream. of(a)); } public ExceptionalStream prepend(final Collection c) { return prepend(ExceptionalStream. of(c)); } /** * * @param s * @return */ public ExceptionalStream prepend(final ExceptionalStream s) { return concat(s, this); } @SafeVarargs public final ExceptionalStream append(final T... a) { return append(ExceptionalStream. of(a)); } public ExceptionalStream append(final Collection c) { return append(ExceptionalStream. of(c)); } /** * * @param s * @return */ public ExceptionalStream append(final ExceptionalStream s) { return concat(this, s); } @SafeVarargs public final ExceptionalStream appendIfEmpty(final T... a) { return appendIfEmpty(Arrays.asList(a)); } public ExceptionalStream appendIfEmpty(final Collection c) { if (N.isNullOrEmpty(c)) { return newStream(elements, closeHandlers); } return newStream(new ExceptionalIterator() { private ExceptionalIterator iter; @Override public boolean hasNext() throws E { if (iter == null) { init(); } return iter.hasNext(); } @Override public T next() throws E { if (iter == null) { init(); } return iter.next(); } @Override public void skip(long n) throws E { if (iter == null) { init(); } iter.skip(n); } @Override public long count() throws E { if (iter == null) { init(); } return iter.count(); } private void init() throws E { if (iter == null) { if (elements.hasNext()) { iter = elements; } else { iter = ExceptionalIterator.wrap(c.iterator()); } } } }, closeHandlers); } /** * Append if empty. * * @param supplier * @return * @throws E the e */ public ExceptionalStream appendIfEmpty(final Supplier> supplier) { final Holder> holder = new Holder<>(); return newStream(new ExceptionalIterator() { private ExceptionalIterator iter; @Override public boolean hasNext() throws E { if (iter == null) { init(); } return iter.hasNext(); } @Override public T next() throws E { if (iter == null) { init(); } return iter.next(); } @Override public void skip(long n) throws E { if (iter == null) { init(); } iter.skip(n); } @Override public long count() throws E { if (iter == null) { init(); } return iter.count(); } private void init() throws E { if (iter == null) { if (elements.hasNext()) { iter = elements; } else { final ExceptionalStream s = supplier.get(); holder.setValue(s); iter = s.iterator(); } } } }, closeHandlers).onClose(new Throwables.Runnable() { @Override public void run() throws E { close(holder); } }); } void close(Holder> holder) throws E { if (holder.value() != null) { holder.value().close(); } } public Optional applyIfNotEmpty(final Throwables.Function, R, E> func) throws E { try { if (elements.hasNext()) { return Optional.of(func.apply(this)); } else { return Optional.empty(); } } finally { close(); } } public OrElse acceptIfNotEmpty(Throwables.Consumer, E> action) throws E { try { if (elements.hasNext()) { action.accept(this); return OrElse.TRUE; } } finally { close(); } return OrElse.FALSE; } /** * * @param action * @return */ public ExceptionalStream peek(final Throwables.Consumer action) { checkArgNotNull(action, "action"); return newStream(new ExceptionalIterator() { @Override public boolean hasNext() throws E { return elements.hasNext(); } @Override public T next() throws E { final T next = elements.next(); action.accept(next); return next; } }, sorted, comparator, closeHandlers); } /** * Returns ExceptionalStream of {@code List} with consecutive sub sequences of the elements, each of the same size (the final sequence may be smaller). * * * @param chunkSize the desired size of each sub sequence (the last may be smaller). * @return */ public ExceptionalStream, E> splitToList(final int chunkSize) { return split(chunkSize, Factory. ofList()); } /** * Returns ExceptionalStream of {@code Set} with consecutive sub sequences of the elements, each of the same size (the final sequence may be smaller). * * * @param chunkSize the desired size of each sub sequence (the last may be smaller). * @return */ public ExceptionalStream, E> splitToSet(final int chunkSize) { return split(chunkSize, Factory. ofSet()); } /** * Returns ExceptionalStream of {@code C} with consecutive sub sequences of the elements, each of the same size (the final sequence may be smaller). * * * @param * @param chunkSize the desired size of each sub sequence (the last may be smaller). * @param collectionSupplier * @return */ public > ExceptionalStream split(final int chunkSize, final IntFunction collectionSupplier) { checkArgPositive(chunkSize, "chunkSize"); checkArgNotNull(collectionSupplier, "collectionSupplier"); return newStream(new ExceptionalIterator() { @Override public boolean hasNext() throws E { return elements.hasNext(); } @Override public C next() throws E { if (hasNext() == false) { throw new NoSuchElementException(); } final C result = collectionSupplier.apply(chunkSize); int cnt = 0; while (cnt++ < chunkSize && elements.hasNext()) { result.add(elements.next()); } return result; } @Override public long count() throws E { final long len = elements.count(); return len % chunkSize == 0 ? len / chunkSize : len / chunkSize + 1; } @Override public void skip(long n) throws E { checkArgNotNegative(n, "n"); elements.skip(n > Long.MAX_VALUE / chunkSize ? Long.MAX_VALUE : n * chunkSize); } }, closeHandlers); } /** * * @param * @param * @param chunkSize the desired size of each sub sequence (the last may be smaller). * @param collector * @return */ public ExceptionalStream split(final int chunkSize, final Collector collector) { checkArgPositive(chunkSize, "chunkSize"); checkArgNotNull(collector, "collector"); final Supplier supplier = collector.supplier(); final BiConsumer accumulator = collector.accumulator(); final Function finisher = collector.finisher(); return newStream(new ExceptionalIterator() { @Override public boolean hasNext() throws E { return elements.hasNext(); } @Override public R next() throws E { if (hasNext() == false) { throw new NoSuchElementException(); } final A container = supplier.get(); int cnt = 0; while (cnt++ < chunkSize && elements.hasNext()) { accumulator.accept(container, elements.next()); } return finisher.apply(container); } @Override public long count() throws E { final long len = elements.count(); return len % chunkSize == 0 ? len / chunkSize : len / chunkSize + 1; } @Override public void skip(long n) throws E { checkArgNotNegative(n, "n"); elements.skip(n > Long.MAX_VALUE / chunkSize ? Long.MAX_VALUE : n * chunkSize); } }, closeHandlers); } /** * Sliding to list. * * @param windowSize * @param increment * @return */ public ExceptionalStream, E> slidingToList(final int windowSize, final int increment) { return sliding(windowSize, increment, Factory. ofList()); } /** * Sliding to set. * * @param windowSize * @param increment * @return */ public ExceptionalStream, E> slidingToSet(final int windowSize, final int increment) { return sliding(windowSize, increment, Factory. ofSet()); } /** * * @param * @param windowSize * @param increment * @param collectionSupplier * @return */ public > ExceptionalStream sliding(final int windowSize, final int increment, final IntFunction collectionSupplier) { checkArgument(windowSize > 0 && increment > 0, "windowSize=%s and increment=%s must be bigger than 0", windowSize, increment); checkArgNotNull(collectionSupplier, "collectionSupplier"); return newStream(new ExceptionalIterator() { private Deque queue = null; private boolean toSkip = false; @Override public boolean hasNext() throws E { if (toSkip) { int skipNum = increment - windowSize; while (skipNum-- > 0 && elements.hasNext()) { elements.next(); } toSkip = false; } return elements.hasNext(); } @Override public C next() throws E { if (hasNext() == false) { throw new NoSuchElementException(); } if (queue == null) { queue = new ArrayDeque<>(N.max(0, windowSize - increment)); } final C result = collectionSupplier.apply(windowSize); int cnt = 0; if (queue.size() > 0 && increment < windowSize) { cnt = queue.size(); for (T e : queue) { result.add(e); } if (queue.size() <= increment) { queue.clear(); } else { for (int i = 0; i < increment; i++) { queue.removeFirst(); } } } T next = null; while (cnt++ < windowSize && elements.hasNext()) { next = elements.next(); result.add(next); if (cnt > increment) { queue.add(next); } } toSkip = increment > windowSize; return result; } @Override public long count() throws E { final int prevSize = increment >= windowSize ? 0 : (queue == null ? 0 : queue.size()); final long len = prevSize + elements.count(); if (len == prevSize) { return 0; } else if (len <= windowSize) { return 1; } else { final long rlen = len - windowSize; return 1 + (rlen % increment == 0 ? rlen / increment : rlen / increment + 1); } } @Override public void skip(long n) throws E { checkArgNotNegative(n, "n"); if (n == 0) { return; } if (increment >= windowSize) { elements.skip(n > Long.MAX_VALUE / increment ? Long.MAX_VALUE : n * increment); } else { if (N.isNullOrEmpty(queue)) { final long m = ((n - 1) > Long.MAX_VALUE / increment ? Long.MAX_VALUE : (n - 1) * increment); elements.skip(m); } else { final long m = (n > Long.MAX_VALUE / increment ? Long.MAX_VALUE : n * increment); final int prevSize = increment >= windowSize ? 0 : (queue == null ? 0 : queue.size()); if (m < prevSize) { for (int i = 0; i < m; i++) { queue.removeFirst(); } } else { if (queue != null) { queue.clear(); } elements.skip(m - prevSize); } } if (queue == null) { queue = new ArrayDeque<>(windowSize); } int cnt = queue.size(); while (cnt++ < windowSize && elements.hasNext()) { queue.add(elements.next()); } } } }, closeHandlers); } /** * * @param * @param * @param windowSize * @param increment * @param collector * @return */ public ExceptionalStream sliding(final int windowSize, final int increment, final Collector collector) { checkArgument(windowSize > 0 && increment > 0, "windowSize=%s and increment=%s must be bigger than 0", windowSize, increment); checkArgNotNull(collector, "collector"); final Supplier supplier = collector.supplier(); final BiConsumer accumulator = collector.accumulator(); final Function finisher = collector.finisher(); return newStream(new ExceptionalIterator() { private Deque queue = null; private boolean toSkip = false; @Override public boolean hasNext() throws E { if (toSkip) { int skipNum = increment - windowSize; while (skipNum-- > 0 && elements.hasNext()) { elements.next(); } toSkip = false; } return elements.hasNext(); } @Override public R next() throws E { if (hasNext() == false) { throw new NoSuchElementException(); } if (increment < windowSize && queue == null) { queue = new ArrayDeque<>(windowSize - increment); } final A container = supplier.get(); int cnt = 0; if (increment < windowSize && queue.size() > 0) { cnt = queue.size(); for (T e : queue) { accumulator.accept(container, e); } if (queue.size() <= increment) { queue.clear(); } else { for (int i = 0; i < increment; i++) { queue.removeFirst(); } } } T next = null; while (cnt++ < windowSize && elements.hasNext()) { next = elements.next(); accumulator.accept(container, next); if (cnt > increment) { queue.add(next); } } toSkip = increment > windowSize; return finisher.apply(container); } @Override public long count() throws E { final int prevSize = increment >= windowSize ? 0 : (queue == null ? 0 : queue.size()); final long len = prevSize + elements.count(); if (len == prevSize) { return 0; } else if (len <= windowSize) { return 1; } else { final long rlen = len - windowSize; return 1 + (rlen % increment == 0 ? rlen / increment : rlen / increment + 1); } } @Override public void skip(long n) throws E { checkArgNotNegative(n, "n"); if (n == 0) { return; } if (increment >= windowSize) { elements.skip(n > Long.MAX_VALUE / increment ? Long.MAX_VALUE : n * increment); } else { if (N.isNullOrEmpty(queue)) { final long m = ((n - 1) > Long.MAX_VALUE / increment ? Long.MAX_VALUE : (n - 1) * increment); elements.skip(m); } else { final long m = (n > Long.MAX_VALUE / increment ? Long.MAX_VALUE : n * increment); final int prevSize = increment >= windowSize ? 0 : (queue == null ? 0 : queue.size()); if (m < prevSize) { for (int i = 0; i < m; i++) { queue.removeFirst(); } } else { if (queue != null) { queue.clear(); } elements.skip(m - prevSize); } } if (queue == null) { queue = new ArrayDeque<>(windowSize); } int cnt = queue.size(); while (cnt++ < windowSize && elements.hasNext()) { queue.add(elements.next()); } } } }, closeHandlers); } /** * * @param n * @return */ public ExceptionalStream skip(final long n) { checkArgNotNegative(n, "n"); return newStream(new ExceptionalIterator() { private boolean skipped = false; @Override public boolean hasNext() throws E { if (skipped == false) { skipped = true; skip(n); } return elements.hasNext(); } @Override public T next() throws E { if (skipped == false) { skipped = true; skip(n); } return elements.next(); } }, sorted, comparator, closeHandlers); } /** * * @return */ public ExceptionalStream skipNull() { return filter(Fnn. notNull()); } /** * * @param maxSize * @return */ public ExceptionalStream limit(final long maxSize) { checkArgNotNegative(maxSize, "maxSize"); return newStream(new ExceptionalIterator() { private long cnt = 0; @Override public boolean hasNext() throws E { return cnt < maxSize && elements.hasNext(); } @Override public T next() throws E { if (cnt >= maxSize) { throw new NoSuchElementException(); } cnt++; return elements.next(); } }, sorted, comparator, closeHandlers); } // /** // * // * @param from // * @param to // * @return // * @deprecated // */ // @Deprecated // public ExceptionalStream slice(final long from, final long to) { // checkArgNotNegative(from, "from"); // checkArgNotNegative(to, "to"); // checkArgument(to >= from, "'to' can't be less than `from`"); // // return from == 0 ? limit(to) : skip(from).limit(to - from); // } public ExceptionalStream shuffled() { return shuffled(RAND); } public ExceptionalStream shuffled(final Random rnd) { return lazyLoad(new Function() { @Override public Object[] apply(final Object[] a) { N.shuffle(a, rnd); return a; } }, false, null); } public ExceptionalStream rotated(final int distance) { if (distance == 0) { return newStream(elements, closeHandlers); } return newStream(new ExceptionalIterator() { private boolean initialized = false; private T[] aar; private int len; private int start; private int cnt = 0; @Override public boolean hasNext() throws E { if (initialized == false) { init(); } return cnt < len; } @Override public T next() throws E { if (hasNext() == false) { throw new NoSuchElementException(); } return aar[(start + cnt++) % len]; } @Override public long count() throws E { if (initialized == false) { init(); } return len - cnt; } @Override public void skip(long n) throws E { if (initialized == false) { init(); } cnt = n < len - cnt ? cnt + (int) n : len; } private void init() throws E { if (initialized == false) { initialized = true; aar = (T[]) ExceptionalStream.this.toArray(); len = aar.length; if (len > 0) { start = distance % len; if (start < 0) { start += len; } start = len - start; } } } }); } /** * * @return */ public ExceptionalStream sorted() { return sorted(Comparators.NATURAL_ORDER); } /** * * @return */ public ExceptionalStream reverseSorted() { return sorted(Comparators.REVERSED_ORDER); } /** * * @param comparator * @return */ public ExceptionalStream sorted(final Comparator comparator) { final Comparator cmp = comparator == null ? Comparators.NATURAL_ORDER : comparator; if (sorted && cmp == this.comparator) { return newStream(elements, sorted, comparator, closeHandlers); } return lazyLoad(new Function() { @Override public Object[] apply(final Object[] a) { N.sort((T[]) a, cmp); return a; } }, true, cmp); } /** * * @param keyMapper * @return */ @SuppressWarnings("rawtypes") public ExceptionalStream 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); } /** * * @param op * @param sorted * @param cmp * @return */ private ExceptionalStream lazyLoad(final Function op, final boolean sorted, final Comparator cmp) { return newStream(new ExceptionalIterator() { private boolean initialized = false; private T[] aar; private int cursor = 0; private int len; @Override public boolean hasNext() throws E { if (initialized == false) { init(); } return cursor < len; } @Override public T next() throws E { if (initialized == false) { init(); } if (cursor >= len) { throw new NoSuchElementException(); } return aar[cursor++]; } @Override public long count() throws E { if (initialized == false) { init(); } return len - cursor; } @Override public void skip(long n) throws E { checkArgNotNegative(n, "n"); if (initialized == false) { init(); } cursor = n > len - cursor ? len : cursor + (int) n; } private void init() throws E { if (initialized == false) { initialized = true; aar = (T[]) op.apply(ExceptionalStream.this.toArray()); len = aar.length; } } }, sorted, cmp, closeHandlers); } public ExceptionalStream intersperse(final T delimiter) { return newStream(new ExceptionalIterator() { private final ExceptionalIterator iter = iterator(); private boolean toInsert = false; @Override public boolean hasNext() throws E { return iter.hasNext(); } @Override public T next() throws E { if (hasNext() == false) { throw new NoSuchElementException(); } if (toInsert) { toInsert = false; return delimiter; } else { final T res = iter.next(); toInsert = true; return res; } } }); } /** * * @return */ @Beta public ExceptionalStream, E> indexed() { return map(new Throwables.Function, E>() { private final MutableLong idx = new MutableLong(0); @Override public Indexed apply(T t) { return Indexed.of(t, idx.getAndIncrement()); } }); } /** * * @param * @param * @param b * @param zipFunction * @return */ @IntermediateOp public ExceptionalStream zipWith(final ExceptionalStream b, final Throwables.BiFunction zipFunction) { return zip(this, b, zipFunction); } /** * * @param * @param * @param b * @param valueForNoneA * @param valueForNoneB * @param zipFunction * @return */ @IntermediateOp public ExceptionalStream zipWith(final ExceptionalStream b, final T valueForNoneA, final T2 valueForNoneB, final Throwables.BiFunction zipFunction) { return zip(this, b, valueForNoneA, valueForNoneB, zipFunction); } /** * * @param * @param * @param * @param b * @param c * @param zipFunction * @return */ @IntermediateOp public ExceptionalStream zipWith(final ExceptionalStream b, final ExceptionalStream c, final Throwables.TriFunction zipFunction) { return zip(this, b, c, zipFunction); } /** * * @param * @param * @param * @param b * @param c * @param valueForNoneA * @param valueForNoneB * @param valueForNoneC * @param zipFunction * @return */ @IntermediateOp public ExceptionalStream zipWith(final ExceptionalStream b, final ExceptionalStream c, final T valueForNoneA, final T2 valueForNoneB, final T3 valueForNoneC, final Throwables.TriFunction zipFunction) { return zip(this, b, c, valueForNoneA, valueForNoneB, valueForNoneC, zipFunction); } @IntermediateOp public ExceptionalStream, E> crossJoin(final Collection c) { return crossJoin(c, Fnn., E> ff(Fn. tuple2())); } @IntermediateOp public ExceptionalStream crossJoin(final Collection c, final Throwables.BiFunction func) { return flatMap(new Throwables.Function, E>() { @Override public ExceptionalStream apply(final T t) throws E { return ExceptionalStream. of(c).map(new Throwables.Function() { @Override public R apply(U u) throws E { return func.apply(t, u); } }); } }); } /** * * @param * @param b * @param leftKeyMapper * @param rightKeyMapper * @return */ @IntermediateOp public ExceptionalStream, E> innerJoin(final Collection b, final Throwables.Function leftKeyMapper, final Throwables.Function rightKeyMapper) { return flatMap(new Throwables.Function, E>, E>() { private ListMultimap rightKeyMap = null; @Override public ExceptionalStream, E> apply(final T t) throws E { if (rightKeyMap == null) { rightKeyMap = ListMultimap.from(b, rightKeyMapper); } return ExceptionalStream. of(rightKeyMap.get(leftKeyMapper.apply(t))).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { return Pair.of(t, u); } }); } }); } /** * * @param * @param b * @param predicate * @return */ @IntermediateOp public ExceptionalStream, E> innerJoin(final Collection b, final Throwables.BiPredicate predicate) { return flatMap(new Throwables.Function, E>, E>() { @Override public ExceptionalStream, E> apply(final T t) { return ExceptionalStream. of(b).filter(new Throwables.Predicate() { @Override public boolean test(final U u) throws E { return predicate.test(t, u); } }).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { return Pair.of(t, u); } }); } }); } /** * * @param * @param b * @param leftKeyMapper * @param rightKeyMapper * @return */ @IntermediateOp public ExceptionalStream, E> fullJoin(final Collection b, final Throwables.Function leftKeyMapper, final Throwables.Function rightKeyMapper) { final Map joinedRights = new IdentityHashMap<>(); return flatMap(new Throwables.Function, E>, E>() { private ListMultimap rightKeyMap = null; @Override public ExceptionalStream, E> apply(final T t) throws E { if (rightKeyMap == null) { rightKeyMap = ListMultimap.from(b, rightKeyMapper); } final List values = rightKeyMap.get(leftKeyMapper.apply(t)); return N.isNullOrEmpty(values) ? ExceptionalStream., E> of(Pair.of(t, (U) null)) : ExceptionalStream. of(values).map(new Throwables.Function, E>() { @Override public Pair apply(U u) throws E { joinedRights.put(u, u); return Pair.of(t, u); } }); } }).append(ExceptionalStream. of(b).filter(new Throwables.Predicate() { @Override public boolean test(U u) { return joinedRights.containsKey(u) == false; } }).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { return Pair.of((T) null, u); } })); } /** * * @param * @param b * @param predicate * @return */ @IntermediateOp public ExceptionalStream, E> fullJoin(final Collection b, final Throwables.BiPredicate predicate) { final Map joinedRights = new IdentityHashMap<>(); return flatMap(new Throwables.Function, E>, E>() { @Override public ExceptionalStream, E> apply(final T t) { return ExceptionalStream. of(b).filter(new Throwables.Predicate() { @Override public boolean test(final U u) throws E { return predicate.test(t, u); } }).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { joinedRights.put(u, u); return Pair.of(t, u); } }).appendIfEmpty(Pair.of(t, (U) null)); } }).append(ExceptionalStream. of(b).filter(new Throwables.Predicate() { @Override public boolean test(U u) { return joinedRights.containsKey(u) == false; } }).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { return Pair.of((T) null, u); } })); } /** * * @param * @param b * @param leftKeyMapper * @param rightKeyMapper * @return */ @IntermediateOp public ExceptionalStream, E> leftJoin(final Collection b, final Throwables.Function leftKeyMapper, final Throwables.Function rightKeyMapper) { return flatMap(new Throwables.Function, E>, E>() { private ListMultimap rightKeyMap = null; @Override public ExceptionalStream, E> apply(final T t) throws E { if (rightKeyMap == null) { rightKeyMap = ListMultimap.from(b, rightKeyMapper); } final List values = rightKeyMap.get(leftKeyMapper.apply(t)); return N.isNullOrEmpty(values) ? ExceptionalStream., E> of(Pair.of(t, (U) null)) : ExceptionalStream. of(values).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { return Pair.of(t, u); } }); } }); } /** * * @param * @param b * @param predicate * @return */ @IntermediateOp public ExceptionalStream, E> leftJoin(final Collection b, final Throwables.BiPredicate predicate) { return flatMap(new Throwables.Function, E>, E>() { @Override public ExceptionalStream, E> apply(final T t) { return ExceptionalStream. of(b).filter(new Throwables.Predicate() { @Override public boolean test(final U u) throws E { return predicate.test(t, u); } }).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { return Pair.of(t, u); } }).appendIfEmpty(Pair.of(t, (U) null)); } }); } /** * * @param * @param b * @param leftKeyMapper * @param rightKeyMapper * @return */ @IntermediateOp public ExceptionalStream, E> rightJoin(final Collection b, final Throwables.Function leftKeyMapper, final Throwables.Function rightKeyMapper) { final Map joinedRights = new IdentityHashMap<>(); return flatMap(new Throwables.Function, E>, E>() { private ListMultimap rightKeyMap = null; @Override public ExceptionalStream, E> apply(final T t) throws E { if (rightKeyMap == null) { rightKeyMap = ListMultimap.from(b, rightKeyMapper); } return ExceptionalStream. of(rightKeyMap.get(leftKeyMapper.apply(t))).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { joinedRights.put(u, u); return Pair.of(t, u); } }); } }).append(ExceptionalStream. of(b).filter(new Throwables.Predicate() { @Override public boolean test(U u) { return joinedRights.containsKey(u) == false; } }).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { return Pair.of((T) null, u); } })); } /** * * @param * @param b * @param predicate * @return */ @IntermediateOp public ExceptionalStream, E> rightJoin(final Collection b, final Throwables.BiPredicate predicate) { final Map joinedRights = new IdentityHashMap<>(); return flatMap(new Throwables.Function, E>, E>() { @Override public ExceptionalStream, E> apply(final T t) { return ExceptionalStream. of(b).filter(new Throwables.Predicate() { @Override public boolean test(final U u) throws E { return predicate.test(t, u); } }).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { joinedRights.put(u, u); return Pair.of(t, u); } }); } }).append(ExceptionalStream. of(b).filter(new Throwables.Predicate() { @Override public boolean test(U u) { return joinedRights.containsKey(u) == false; } }).map(new Throwables.Function, E>() { @Override public Pair apply(U u) { return Pair.of((T) null, u); } })); } /** * * @param * @param action * @throws E the e * @throws E2 the e2 */ public void forEach(Throwables.Consumer action) throws E, E2 { checkArgNotNull(action, "action"); assertNotClosed(); try { while (elements.hasNext()) { action.accept(elements.next()); } } finally { close(); } } /** * * @param * @param * @param action * @param onComplete * @throws E the e * @throws E2 the e2 * @throws E3 the e3 */ public void forEach(final Throwables.Consumer action, final Throwables.Runnable onComplete) throws E, E2, E3 { checkArgNotNull(action, "action"); checkArgNotNull(onComplete, "onComplete"); assertNotClosed(); try { while (elements.hasNext()) { action.accept(elements.next()); } onComplete.run(); } finally { close(); } } /** * * @param * @param * @param * @param flatMapper * @param action * @throws E the e * @throws E2 the e2 * @throws E3 the e3 */ public void forEach(final Throwables.Function, E2> flatMapper, final Throwables.BiConsumer action) throws E, E2, E3 { checkArgNotNull(flatMapper, "flatMapper"); checkArgNotNull(action, "action"); assertNotClosed(); Collection c = null; T next = null; try { while (elements.hasNext()) { next = elements.next(); c = flatMapper.apply(next); if (N.notNullOrEmpty(c)) { for (U u : c) { action.accept(next, u); } } } } finally { close(); } } /** * * @param * @param * @param * @param * @param * @param flatMapper * @param flatMapper2 * @param action * @throws E the e * @throws E2 the e2 * @throws E3 the e3 * @throws E4 the e4 */ public void forEach( final Throwables.Function, E2> flatMapper, final Throwables.Function, E3> flatMapper2, final Throwables.TriConsumer action) throws E, E2, E3, E4 { checkArgNotNull(flatMapper, "flatMapper"); checkArgNotNull(flatMapper2, "flatMapper2"); checkArgNotNull(action, "action"); assertNotClosed(); Collection c2 = null; Collection c3 = null; T next = null; try { while (elements.hasNext()) { next = elements.next(); c2 = flatMapper.apply(next); if (N.notNullOrEmpty(c2)) { for (T2 t2 : c2) { c3 = flatMapper2.apply(t2); if (N.notNullOrEmpty(c3)) { for (T3 t3 : c3) { action.accept(next, t2, t3); } } } } } } finally { close(); } } /** * For each pair. * * @param * @param action * @throws E the e * @throws E2 the e2 */ public void forEachPair(final Throwables.BiConsumer action) throws E, E2 { forEachPair(action, 1); } /** * For each pair. * * @param * @param action * @param increment * @throws E the e * @throws E2 the e2 */ public void forEachPair(final Throwables.BiConsumer action, final int increment) throws E, E2 { final int windowSize = 2; checkArgPositive(increment, "increment"); assertNotClosed(); try { boolean isFirst = true; T prev = null; while (elements.hasNext()) { if (increment > windowSize && isFirst == false) { int skipNum = increment - windowSize; while (skipNum-- > 0 && elements.hasNext()) { elements.next(); } if (elements.hasNext() == false) { break; } } if (increment == 1) { action.accept(isFirst ? elements.next() : prev, (prev = (elements.hasNext() ? elements.next() : null))); } else { action.accept(elements.next(), elements.hasNext() ? elements.next() : null); } isFirst = false; } } finally { close(); } } /** * For each triple. * * @param * @param action * @throws E the e * @throws E2 the e2 */ public void forEachTriple(final Throwables.TriConsumer action) throws E, E2 { forEachTriple(action, 1); } /** * For each triple. * * @param * @param action * @param increment * @throws E the e * @throws E2 the e2 */ public void forEachTriple(final Throwables.TriConsumer action, final int increment) throws E, E2 { final int windowSize = 3; checkArgPositive(increment, "increment"); assertNotClosed(); try { boolean isFirst = true; T prev = null; T prev2 = null; while (elements.hasNext()) { if (increment > windowSize && isFirst == false) { int skipNum = increment - windowSize; while (skipNum-- > 0 && elements.hasNext()) { elements.next(); } if (elements.hasNext() == false) { break; } } if (increment == 1) { action.accept(isFirst ? elements.next() : prev2, (prev2 = (isFirst ? (elements.hasNext() ? elements.next() : null) : prev)), (prev = (elements.hasNext() ? elements.next() : null))); } else if (increment == 2) { action.accept(isFirst ? elements.next() : prev, elements.hasNext() ? elements.next() : null, (prev = (elements.hasNext() ? elements.next() : null))); } else { action.accept(elements.next(), elements.hasNext() ? elements.next() : null, elements.hasNext() ? elements.next() : null); } isFirst = false; } } finally { close(); } } /** * * @param comparator * @return * @throws E the e */ public Optional min(Comparator comparator) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return Optional.empty(); } else if (sorted && isSameComparator(comparator, comparator)) { return Optional.of(elements.next()); } comparator = comparator == null ? Comparators.NATURAL_ORDER : comparator; T candidate = elements.next(); T next = null; while (elements.hasNext()) { next = elements.next(); if (comparator.compare(next, candidate) < 0) { candidate = next; } } return Optional.of(candidate); } finally { close(); } } /** * * @param keyMapper * @return * @throws E the e */ @SuppressWarnings("rawtypes") public Optional minBy(final Function keyMapper) throws E { checkArgNotNull(keyMapper, "keyMapper"); assertNotClosed(); try { final Comparator comparator = Fn.comparingBy(keyMapper); return min(comparator); } finally { close(); } } /** * * @param comparator * @return * @throws E the e */ public Optional max(Comparator comparator) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return Optional.empty(); } else if (sorted && isSameComparator(comparator, comparator)) { T next = null; while (elements.hasNext()) { next = elements.next(); } return Optional.of(next); } comparator = comparator == null ? Comparators.NATURAL_ORDER : comparator; T candidate = elements.next(); T next = null; while (elements.hasNext()) { next = elements.next(); if (comparator.compare(next, candidate) > 0) { candidate = next; } } return Optional.of(candidate); } finally { close(); } } /** * * @param keyMapper * @return * @throws E the e */ @SuppressWarnings("rawtypes") public Optional maxBy(final Function keyMapper) throws E { checkArgNotNull(keyMapper, "keyMapper"); assertNotClosed(); try { final Comparator comparator = Fn.comparingBy(keyMapper); return max(comparator); } finally { close(); } } /** * * @param predicate * @return true, if successful * @throws E the e */ public boolean anyMatch(final Throwables.Predicate predicate) throws E { checkArgNotNull(predicate, "predicate"); assertNotClosed(); try { while (elements.hasNext()) { if (predicate.test(elements.next())) { return true; } } return false; } finally { close(); } } /** * * @param predicate * @return true, if successful * @throws E the e */ public boolean allMatch(final Throwables.Predicate predicate) throws E { checkArgNotNull(predicate, "predicate"); assertNotClosed(); try { while (elements.hasNext()) { if (predicate.test(elements.next()) == false) { return false; } } return true; } finally { close(); } } /** * * @param predicate * @return true, if successful * @throws E the e */ public boolean noneMatch(final Throwables.Predicate predicate) throws E { checkArgNotNull(predicate, "predicate"); assertNotClosed(); try { while (elements.hasNext()) { if (predicate.test(elements.next())) { return false; } } return true; } finally { close(); } } /** * * @param atLeast * @param atMost * @param predicate * @return * @throws E */ public boolean nMatch(final long atLeast, final long atMost, final Throwables.Predicate predicate) throws E { checkArgNotNegative(atLeast, "atLeast"); checkArgNotNegative(atMost, "atMost"); checkArgNotNull(predicate, "predicate"); checkArgument(atLeast <= atMost, "'atLeast' must be <= 'atMost'"); assertNotClosed(); long cnt = 0; try { while (elements.hasNext()) { if (predicate.test(elements.next())) { if (++cnt > atMost) { return false; } } } } finally { close(); } return cnt >= atLeast && cnt <= atMost; } @TerminalOp @SafeVarargs public final boolean containsAll(final T... a) throws E { assertNotClosed(); try { if (N.isNullOrEmpty(a)) { return true; } else if (a.length == 1 || (a.length == 2 && N.equals(a[0], a[1]))) { return anyMatch(Fnn. pp(Fn. equal(a[0]))); } else if (a.length == 2) { return filter(new Throwables.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(); } } @TerminalOp public boolean containsAll(final Collection c) throws E { 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(Fnn. pp(Fn. equal(val))); } else { final Set set = c instanceof Set ? (Set) c : N.newHashSet(c); final int distinctCount = set.size(); return filter(new Throwables.Predicate() { @Override public boolean test(T t) { return set.contains(t); } }).distinct().limit(distinctCount).count() == distinctCount; } } finally { close(); } } @TerminalOp @SafeVarargs public final boolean containsAny(final T... a) throws E { assertNotClosed(); try { if (N.isNullOrEmpty(a)) { return false; } else if (a.length == 1 || (a.length == 2 && N.equals(a[0], a[1]))) { return anyMatch(Fnn. pp(Fn. equal(a[0]))); } else if (a.length == 2) { return anyMatch(new Throwables.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); } }); } else { final Set set = N.asSet(a); return anyMatch(new Throwables.Predicate() { @Override public boolean test(T t) { return set.contains(t); } }); } } finally { close(); } } @TerminalOp public boolean containsAny(final Collection c) throws E { assertNotClosed(); try { if (N.isNullOrEmpty(c)) { return false; } else if (c.size() == 1) { final T val = c instanceof List ? ((List) c).get(0) : c.iterator().next(); return anyMatch(Fnn. pp(Fn. equal(val))); } else { final Set set = c instanceof Set ? (Set) c : N.newHashSet(c); return anyMatch(new Throwables.Predicate() { @Override public boolean test(T t) { return set.contains(t); } }); } } finally { close(); } } /** * * @return * @throws E the e */ public Optional first() throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return Optional.empty(); } return Optional.of(elements.next()); } finally { close(); } } /** * * @return * @throws E the e */ public Optional last() throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return Optional.empty(); } T next = elements.next(); while (elements.hasNext()) { next = elements.next(); } return Optional.of(next); } finally { close(); } } /** * * @return * @throws E the e */ public Object[] toArray() throws E { assertNotClosed(); try { return toList().toArray(); } finally { close(); } } /** * * @param * @param generator * @return * @throws E the e */ public A[] toArray(IntFunction generator) throws E { checkArgNotNull(generator, "generator"); assertNotClosed(); try { final List list = toList(); return list.toArray(generator.apply(list.size())); } finally { close(); } } /** * * @return * @throws E the e */ public List toList() throws E { assertNotClosed(); try { final List result = new ArrayList<>(); while (elements.hasNext()) { result.add(elements.next()); } return result; } finally { close(); } } /** * * @return * @throws E the e */ public Set toSet() throws E { assertNotClosed(); try { final Set result = N.newHashSet(); while (elements.hasNext()) { result.add(elements.next()); } return result; } finally { close(); } } /** * * @return * @throws E the e */ public ImmutableList toImmutableList() throws E { return ImmutableList.of(toList()); } /** * * @return * @throws E the e */ public ImmutableSet toImmutableSet() throws E { return ImmutableSet.of(toSet()); } /** * * @param * @param supplier * @return * @throws E the e */ public > C toCollection(final Supplier supplier) throws E { checkArgNotNull(supplier, "supplier"); assertNotClosed(); try { final C result = supplier.get(); while (elements.hasNext()) { result.add(elements.next()); } return result; } finally { close(); } } /** * * @param the key type * @param the value type * @param keyMapper * @param valueMapper * @return * @throws E the e * @throws IllegalStateException if there are duplicated keys. * @see {@link Fn.Fnn#throwingMerger()} * @see {@link Fn.Fnn#replacingMerger()} * @see {@link Fn.Fnn#ignoringMerger()} */ public Map toMap(final Throwables.Function keyMapper, final Throwables.Function valueMapper) throws E, IllegalStateException { return toMap(keyMapper, valueMapper, Suppliers. ofMap()); } /** * * @param the key type * @param the value type * @param * @param keyMapper * @param valueMapper * @param mapFactory * @return * @throws E the e * @throws IllegalStateException if there are duplicated keys. * @see {@link Fn.Fnn#throwingMerger()} * @see {@link Fn.Fnn#replacingMerger()} * @see {@link Fn.Fnn#ignoringMerger()} */ public > M toMap(final Throwables.Function keyMapper, final Throwables.Function valueMapper, final Supplier mapFactory) throws E, IllegalStateException { return toMap(keyMapper, valueMapper, Fnn. throwingMerger(), mapFactory); } /** * * @param the key type * @param the value type * @param keyMapper * @param valueMapper * @param mergeFunction * @return * @throws E the e * @see {@link Fn.Fnn#throwingMerger()} * @see {@link Fn.Fnn#replacingMerger()} * @see {@link Fn.Fnn#ignoringMerger()} */ public Map toMap(final Throwables.Function keyMapper, final Throwables.Function valueMapper, final Throwables.BinaryOperator mergeFunction) throws E { return toMap(keyMapper, valueMapper, mergeFunction, Suppliers. ofMap()); } /** * * @param the key type * @param the value type * @param * @param keyMapper * @param valueMapper * @param mergeFunction * @param mapFactory * @return * @throws E the e * @see {@link Fn.Fnn#throwingMerger()} * @see {@link Fn.Fnn#replacingMerger()} * @see {@link Fn.Fnn#ignoringMerger()} */ public > M toMap(final Throwables.Function keyMapper, final Throwables.Function valueMapper, final Throwables.BinaryOperator mergeFunction, final Supplier mapFactory) throws E { checkArgNotNull(keyMapper, "keyMapper"); checkArgNotNull(valueMapper, "valueMapper"); checkArgNotNull(mergeFunction, "mergeFunction"); checkArgNotNull(mapFactory, "mapFactory"); assertNotClosed(); try { final M result = mapFactory.get(); T next = null; while (elements.hasNext()) { next = elements.next(); Maps.merge(result, keyMapper.apply(next), valueMapper.apply(next), mergeFunction); } return result; } finally { close(); } } /** * * @param the key type * @param * @param * @param keyMapper * @param downstream * @return * @throws E the e */ public Map toMap(final Throwables.Function keyMapper, final Collector downstream) throws E { return toMap(keyMapper, downstream, Suppliers. ofMap()); } /** * * @param the key type * @param * @param * @param * @param keyMapper * @param downstream * @param mapFactory * @return * @throws E the e */ public > M toMap(final Throwables.Function keyMapper, final Collector downstream, final Supplier mapFactory) throws E { return toMap(keyMapper, Fnn. identity(), downstream, mapFactory); } /** * * @param the key type * @param the value type * @param * @param * @param keyMapper * @param valueMapper * @param downstream * @return * @throws E the e */ public Map toMap(final Throwables.Function keyMapper, final Throwables.Function valueMapper, final Collector downstream) throws E { return toMap(keyMapper, valueMapper, downstream, Suppliers. ofMap()); } /** * * @param the key type * @param the value type * @param * @param * @param * @param keyMapper * @param valueMapper * @param downstream * @param mapFactory * @return * @throws E the e */ public > M toMap(final Throwables.Function keyMapper, final Throwables.Function valueMapper, final Collector downstream, final Supplier mapFactory) throws E { checkArgNotNull(keyMapper, "keyMapper"); checkArgNotNull(valueMapper, "valueMapper"); checkArgNotNull(downstream, "downstream"); checkArgNotNull(mapFactory, "mapFactory"); assertNotClosed(); try { final Supplier downstreamSupplier = downstream.supplier(); final BiConsumer downstreamAccumulator = downstream.accumulator(); final Function downstreamFinisher = downstream.finisher(); final M result = mapFactory.get(); final Map tmp = (Map) result; T next = null; K key = null; A container = null; while (elements.hasNext()) { next = elements.next(); key = keyMapper.apply(next); container = tmp.get(key); if (container == null) { container = downstreamSupplier.get(); tmp.put(key, container); } downstreamAccumulator.accept(container, valueMapper.apply(next)); } for (Map.Entry entry : result.entrySet()) { entry.setValue(downstreamFinisher.apply((A) entry.getValue())); } return result; } finally { close(); } } /** * * @param the key type * @param keyMapper * @return * @throws E the e * @see Collectors#groupingBy(Function) */ public Map> groupTo(Throwables.Function keyMapper) throws E { return groupTo(keyMapper, Suppliers.> ofMap()); } /** * * @param the key type * @param * @param keyMapper * @param mapFactory * @return * @throws E the e * @see Collectors#groupingBy(Function, Supplier) */ public >> M groupTo(final Throwables.Function keyMapper, final Supplier mapFactory) throws E { final Throwables.Function valueMapper = Fnn.identity(); return groupTo(keyMapper, valueMapper, mapFactory); } /** * * @param the key type * @param the value type * @param keyMapper * @param valueMapper * @return * @throws E the e */ public Map> groupTo(Throwables.Function keyMapper, Throwables.Function valueMapper) throws E { return groupTo(keyMapper, valueMapper, Suppliers.> ofMap()); } /** * * @param the key type * @param the value type * @param * @param keyMapper * @param valueMapper * @param mapFactory * @return * @throws E the e * @see Collectors#toMultimap(Function, Function, Supplier) */ public >> M groupTo(Throwables.Function keyMapper, Throwables.Function valueMapper, Supplier mapFactory) throws E { checkArgNotNull(keyMapper, "keyMapper"); checkArgNotNull(valueMapper, "valueMapper"); checkArgNotNull(mapFactory, "mapFactory"); assertNotClosed(); try { final M result = mapFactory.get(); T next = null; K key = null; while (elements.hasNext()) { next = elements.next(); key = keyMapper.apply(next); if (result.containsKey(key) == false) { result.put(key, new ArrayList()); } result.get(key).add(valueMapper.apply(next)); } return result; } finally { close(); } } public Multiset toMultiset() throws E { return toMultiset(Suppliers. ofMultiset()); } public Multiset toMultiset(Supplier> supplier) throws E { assertNotClosed(); try { final Multiset result = supplier.get(); while (elements.hasNext()) { result.add(elements.next()); } return result; } finally { close(); } } /** * The first row will be used as column names if its type is array or list, * or obtain the column names from first row if its type is entity or map. * * @return * @throws E * @see {@link N#newDataSet(Collection)} */ public DataSet toDataSet() throws E { return N.newDataSet(toList()); } /** * If the specified {@code columnNames} is null or empty, the first row will be used as column names if its type is array or list, * or obtain the column names from first row if its type is entity or map. * * * @param columnNames * @return * @throws E * @see {@link N#newDataSet(Collection, Collection)} */ public DataSet toDataSet(List columnNames) throws E { return N.newDataSet(columnNames, toList()); } /** * * @return * @throws E the e */ public long count() throws E { assertNotClosed(); try { return elements.count(); } finally { close(); } } /** * * @return * @throws DuplicatedResultException if there are more than one elements. * @throws E the e */ public Optional onlyOne() throws DuplicatedResultException, E { assertNotClosed(); try { Optional result = Optional.empty(); if (elements.hasNext()) { result = Optional.of(elements.next()); if (elements.hasNext()) { throw new DuplicatedResultException("There are at least two elements: " + Strings.concat(result.get(), ", ", elements.next())); } } return result; } finally { close(); } } /** * * @param func * @return * @throws E the e */ public OptionalLong sumInt(Throwables.ToIntFunction func) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return OptionalLong.empty(); } long sum = 0; while (elements.hasNext()) { sum += func.applyAsInt(elements.next()); } return OptionalLong.of(sum); } finally { close(); } } /** * * @param func * @return * @throws E the e */ public OptionalLong sumLong(Throwables.ToLongFunction func) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return OptionalLong.empty(); } long sum = 0; while (elements.hasNext()) { sum += func.applyAsLong(elements.next()); } return OptionalLong.of(sum); } finally { close(); } } /** * * @param func * @return * @throws E the e */ public OptionalDouble sumDouble(Throwables.ToDoubleFunction func) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return OptionalDouble.empty(); } final KahanSummation summation = new KahanSummation(); while (elements.hasNext()) { summation.add(func.applyAsDouble(elements.next())); } return OptionalDouble.of(summation.sum()); } finally { close(); } } /** * * @param func * @return * @throws E the e */ public OptionalDouble averageInt(Throwables.ToIntFunction func) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return OptionalDouble.empty(); } long sum = 0; long count = 0; while (elements.hasNext()) { sum += func.applyAsInt(elements.next()); count++; } return OptionalDouble.of(((double) sum) / count); } finally { close(); } } /** * * @param func * @return * @throws E the e */ public OptionalDouble averageLong(Throwables.ToLongFunction func) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return OptionalDouble.empty(); } long sum = 0; long count = 0; while (elements.hasNext()) { sum += func.applyAsLong(elements.next()); count++; } return OptionalDouble.of(((double) sum) / count); } finally { close(); } } /** * * @param func * @return * @throws E the e */ public OptionalDouble averageDouble(Throwables.ToDoubleFunction func) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return OptionalDouble.empty(); } final KahanSummation summation = new KahanSummation(); while (elements.hasNext()) { summation.add(func.applyAsDouble(elements.next())); } return summation.average(); } finally { close(); } } /** * * @param * @param accumulator * @return * @throws E the e * @throws E2 the e2 */ public Optional reduce(Throwables.BinaryOperator accumulator) throws E, E2 { checkArgNotNull(accumulator, "accumulator"); assertNotClosed(); try { if (elements.hasNext() == false) { return Optional.empty(); } T result = elements.next(); while (elements.hasNext()) { result = accumulator.apply(result, elements.next()); } return Optional.of(result); } finally { close(); } } /** * * @param * @param * @param identity * @param accumulator * @return * @throws E the e * @throws E2 the e2 */ public U reduce(final U identity, final Throwables.BiFunction accumulator) throws E, E2 { checkArgNotNull(accumulator, "accumulator"); assertNotClosed(); try { U result = identity; while (elements.hasNext()) { result = accumulator.apply(result, elements.next()); } return result; } finally { close(); } } /** * * @param * @param * @param * @param supplier * @param accumulator * @return * @throws E the e * @throws E2 the e2 * @throws E3 the e3 */ public R collect(final Throwables.Supplier supplier, final Throwables.BiConsumer accumulator) throws E, E2, E3 { checkArgNotNull(supplier, "supplier"); checkArgNotNull(accumulator, "accumulator"); assertNotClosed(); try { final R result = supplier.get(); while (elements.hasNext()) { accumulator.accept(result, elements.next()); } return result; } finally { close(); } } /** * * @param * @param * @param * @param * @param * @param supplier * @param accumulator * @param finisher * @return * @throws E the e * @throws E2 the e2 * @throws E3 the e3 * @throws E4 the e4 */ public RR collect(final Throwables.Supplier supplier, final Throwables.BiConsumer accumulator, final Throwables.Function finisher) throws E, E2, E3, E4 { checkArgNotNull(supplier, "supplier"); checkArgNotNull(accumulator, "accumulator"); checkArgNotNull(finisher, "finisher"); assertNotClosed(); try { final R result = supplier.get(); while (elements.hasNext()) { accumulator.accept(result, elements.next()); } return finisher.apply(result); } finally { close(); } } /** * * @param * @param * @param collector * @return * @throws E the e */ public R collect(final Collector collector) throws E { checkArgNotNull(collector, "collector"); assertNotClosed(); try { final A container = collector.supplier().get(); final BiConsumer accumulator = collector.accumulator(); while (elements.hasNext()) { accumulator.accept(container, elements.next()); } return collector.finisher().apply(container); } finally { close(); } } /** * Collect and then. * * @param * @param * @param * @param * @param collector * @param func * @return * @throws E the e * @throws E2 the e2 */ public RR collectAndThen(final Collector collector, final Throwables.Function func) throws E, E2 { checkArgNotNull(collector, "collector"); checkArgNotNull(func, "func"); assertNotClosed(); return func.apply(collect(collector)); } public String join(final CharSequence delimiter) throws E { return join(delimiter, "", ""); } public String join(CharSequence delimiter, CharSequence prefix, CharSequence suffix) throws E { assertNotClosed(); try { final Joiner joiner = Joiner.with(delimiter, prefix, suffix).reuseCachedBuffer(true); while (elements.hasNext()) { joiner.append(elements.next()); } return joiner.toString(); } finally { close(); } } @Beta public void println() throws E { N.println(join(", ", "[", "]")); } /** * * @param * @param transfer * @return * @throws E the e */ @Beta public ExceptionalStream __(Function, ExceptionalStream> transfer) { checkArgNotNull(transfer, "transfer"); return transfer.apply(this); } /** * * @param closeHandler * @return */ public ExceptionalStream onClose(final Throwables.Runnable closeHandler) { checkArgNotNull(closeHandler, "closeHandler"); final Deque> newCloseHandlers = new ArrayDeque<>(N.size(closeHandlers) + 1); newCloseHandlers.add(new Throwables.Runnable() { private volatile boolean isClosed = false; @Override public void run() throws E { if (isClosed) { return; } isClosed = true; closeHandler.run(); } }); if (N.notNullOrEmpty(this.closeHandlers)) { newCloseHandlers.addAll(this.closeHandlers); } return newStream(elements, newCloseHandlers); } /** * * @throws E the e */ @Override public synchronized void close() throws E { if (isClosed) { return; } if (N.isNullOrEmpty(closeHandlers)) { isClosed = true; return; } // // Only mark the stream closed if closeHandlers are not empty. // if (isClosed || N.isNullOrEmpty(closeHandlers)) { // return; // } logger.info("Closing ExceptionalStream"); isClosed = true; Throwable ex = null; for (Throwables.Runnable closeHandler : closeHandlers) { try { closeHandler.run(); } catch (Exception e) { if (ex == null) { ex = e; } else { if (ex instanceof RuntimeException && !(ex instanceof RuntimeException)) { e.addSuppressed(ex); ex = e; } else { ex.addSuppressed(e); } } } } if (ex != null) { if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { throw (E) ex; } } } ExceptionalIterator iterator() { return elements; } /** * Assert not closed. */ void assertNotClosed() { if (isClosed) { throw new IllegalStateException("This stream has been closed"); } } /** * Check arg positive. * * @param arg * @param argNameOrErrorMsg * @return */ private int checkArgPositive(final int arg, final String argNameOrErrorMsg) { if (arg <= 0) { try { N.checkArgPositive(arg, argNameOrErrorMsg); } finally { try { close(); } catch (Exception e) { throw N.toRuntimeException(e); } } } return arg; } /** * Check arg not negative. * * @param arg * @param argNameOrErrorMsg * @return */ private long checkArgNotNegative(final long arg, final String argNameOrErrorMsg) { if (arg < 0) { try { N.checkArgNotNegative(arg, argNameOrErrorMsg); } finally { try { close(); } catch (Exception e) { throw N.toRuntimeException(e); } } } return arg; } /** * Check arg not null. * * @param * @param obj * @param errorMessage * @return */ private ARG checkArgNotNull(final ARG obj, final String errorMessage) { if (obj == null) { try { N.checkArgNotNull(obj, errorMessage); } finally { try { close(); } catch (Exception e) { throw N.toRuntimeException(e); } } } return obj; } /** * * @param b * @param errorMessage */ private void checkArgument(boolean b, String errorMessage) { if (!b) { try { N.checkArgument(b, errorMessage); } finally { try { close(); } catch (Exception e) { throw N.toRuntimeException(e); } } } } /** * * @param b * @param errorMessageTemplate * @param p1 * @param p2 */ private void checkArgument(boolean b, String errorMessageTemplate, int p1, int p2) { if (!b) { try { N.checkArgument(b, errorMessageTemplate, p1, p2); } finally { try { close(); } catch (Exception e) { throw N.toRuntimeException(e); } } } } /** * * @param * @param * @param iter * @return */ static ExceptionalStream newStream(final ExceptionalIterator iter) { return new ExceptionalStream<>(iter, null); } /** * * @param * @param * @param iter * @param closeHandlers * @return */ static ExceptionalStream newStream(final ExceptionalIterator iter, final Deque> closeHandlers) { return new ExceptionalStream<>(iter, closeHandlers); } /** * * @param * @param * @param iter * @param sorted * @param comparator * @param closeHandlers * @return */ static ExceptionalStream newStream(final ExceptionalIterator iter, final boolean sorted, final Comparator comparator, final Deque> closeHandlers) { return new ExceptionalStream<>(iter, sorted, comparator, closeHandlers); } /** * * @param obj * @return */ static Object hashKey(Object obj) { return obj == null || obj.getClass().isArray() == false ? obj : Wrapper.of(obj); } /** * Checks if is same comparator. * * @param a * @param b * @return true, if is same comparator */ static boolean isSameComparator(Comparator a, Comparator b) { return a == b || (a == null && b == Comparators.NATURAL_ORDER) || (b == null && a == Comparators.NATURAL_ORDER); } /** * The Class StreamE. * * @param * @param */ public static final class StreamE extends ExceptionalStream { /** * Instantiates a new stream E. * * @param iter * @param sorted * @param comparator * @param closeHandlers */ StreamE(ExceptionalIterator iter, boolean sorted, Comparator comparator, Deque> closeHandlers) { super(iter, sorted, comparator, closeHandlers); } } /** * The Class ExceptionalIterator. * * @param * @param */ static abstract class ExceptionalIterator { /** The Constant EMPTY. */ @SuppressWarnings("rawtypes") private static final ExceptionalIterator EMPTY = new ExceptionalIterator() { @Override public boolean hasNext() throws Exception { return false; } @Override public Object next() throws Exception { throw new NoSuchElementException(); } }; public static ExceptionalIterator wrap(final Iterator iter) { if (iter == null) { return EMPTY; } return new ExceptionalIterator() { @Override public boolean hasNext() throws E { return iter.hasNext(); } @Override public T next() throws E { return iter.next(); } }; } /** * Lazy evaluation. * * @param * @param * @param iteratorSupplier * @return */ public static ExceptionalIterator of(final Throwables.Supplier, E> iteratorSupplier) { N.checkArgNotNull(iteratorSupplier, "iteratorSupplier"); return new ExceptionalIterator() { private ExceptionalIterator iter = null; private boolean isInitialized = false; @Override public boolean hasNext() throws E { if (isInitialized == false) { init(); } return iter.hasNext(); } @Override public T next() throws E { if (isInitialized == false) { init(); } return iter.next(); } @Override public void skip(long n) throws E { N.checkArgNotNegative(n, "n"); if (isInitialized == false) { init(); } iter.skip(n); } @Override public long count() throws E { if (isInitialized == false) { init(); } return iter.count(); } @Override public void close() throws E { if (isInitialized == false) { init(); } iter.close(); } private void init() throws E { if (isInitialized == false) { isInitialized = true; iter = iteratorSupplier.get(); } } }; } /** * Lazy evaluation. * * @param * @param * @param arraySupplier * @return */ public static ExceptionalIterator oF(final Throwables.Supplier arraySupplier) { N.checkArgNotNull(arraySupplier, "arraySupplier"); return new ExceptionalIterator() { private T[] a; private int len; private int position = 0; private boolean isInitialized = false; @Override public boolean hasNext() throws E { if (isInitialized == false) { init(); } return position < len; } @Override public T next() throws E { if (isInitialized == false) { init(); } if (position >= len) { throw new NoSuchElementException(); } return a[position++]; } @Override public long count() throws E { if (isInitialized == false) { init(); } return len - position; } @Override public void skip(long n) throws E { N.checkArgNotNegative(n, "n"); if (isInitialized == false) { init(); } if (n <= 0) { return; } else if (n > len - position) { position = len; } position += n; } private void init() throws E { if (isInitialized == false) { isInitialized = true; a = arraySupplier.get(); len = N.len(a); } } }; } /** * Checks for next. * * @return true, if successful * @throws E the e */ public abstract boolean hasNext() throws E; /** * * @return * @throws E the e */ public abstract T next() throws E; /** * * @param n * @throws E the e */ public void skip(long n) throws E { N.checkArgNotNegative(n, "n"); while (n-- > 0 && hasNext()) { next(); } } /** * * @return * @throws E the e */ public long count() throws E { long result = 0; while (hasNext()) { next(); result++; } return result; } /** * * @throws E the e */ public void close() throws E { // Nothing to do by default. } } }