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

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

There is a newer version: 1.10.1
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.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.Executor;

import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.annotation.SequentialOnly;
import com.landawn.abacus.exception.DuplicatedResultException;
import com.landawn.abacus.exception.UncheckedSQLException;
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.Suppliers;
import com.landawn.abacus.util.StringUtil.Strings;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.u.OptionalDouble;
import com.landawn.abacus.util.u.OptionalInt;
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;
import com.landawn.abacus.util.stream.ObjIteratorEx;
import com.landawn.abacus.util.stream.Stream;

/**
 * The Stream will be automatically closed after execution(A terminal method is executed/triggered).
 * 
 * @since 1.3
 * 
 * @author Haiyang Li
 */
@SequentialOnly
public class ExceptionalStream implements AutoCloseable {
    static final Logger logger = LoggerFactory.getLogger(ExceptionalStream.class);

    private final ExceptionalIterator elements;
    private final boolean sorted;
    private final Comparator comparator;
    private final Deque> closeHandlers;
    private boolean isClosed = false;

    ExceptionalStream(final ExceptionalIterator iter) {
        this(iter, false, null, null);
    }

    ExceptionalStream(final ExceptionalIterator iter, final Deque> closeHandlers) {
        this(iter, false, null, 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;
    }

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

    public static  ExceptionalStream just(final T e) {
        return of(e);
    }

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

        return of(e);
    }

    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;
            }
        });
    }

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

        return of(c.iterator());
    }

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

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

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

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

        return of(iterable.iterator());
    }

    public static  ExceptionalStream, E> of(final Map m) {
        if (m == null) {
            return empty();
        }

        return of(m.entrySet());
    }

    public static  ExceptionalStream of(final Stream stream) {
        if (stream == null) {
            return empty();
        }

        final ExceptionalIterator iter = new ExceptionalIterator() {
            private Stream s = stream;
            private Iterator 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 (iter == null) {
                    s = s.skip(n);
                } else {
                    super.skip(n);
                }
            }

            @Override
            public long count() throws E {
                if (iter == null) {
                    return s.count();
                } else {
                    return super.count();
                }
            }

            @Override
            public void close() throws E {
                s.close();
            }

            private void init() {
                if (isInitialized == false) {
                    isInitialized = true;
                    iter = stream.iterator();
                }
            }
        };

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

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

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

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

    public static  ExceptionalStream, E> of(final Map m, final Class exceptionType) {
        return of(m);
    }

    public static  ExceptionalStream of(final Stream stream, final Class exceptionType) {
        return of(stream);
    }

    public static  ExceptionalStream iterate(final Try.BooleanSupplier hasNext,
            final Try.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();
            }
        });
    }

    public static  ExceptionalStream iterate(final T init, final Try.BooleanSupplier hasNext,
            final Try.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);
            }
        });
    }

    public static  ExceptionalStream iterate(final T init, final Try.Predicate hasNext,
            final Try.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;
            }
        });
    }

    public static  ExceptionalStream iterate(final T init, final Try.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 lines(final File file) {
        return lines(file, Charsets.UTF_8);
    }

    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 Try.Runnable() {
            @Override
            public void run() throws IOException {
                iter.close();
            }
        });
    }

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

    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 Try.Runnable() {
            @Override
            public void run() throws IOException {
                iter.close();
            }
        });
    }

    public static ExceptionalStream lines(final Reader reader) {
        N.checkArgNotNull(reader, "reader");

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

    private static ExceptionalIterator createLazyLineIterator(final File file, final Path path, final Charset charset, final Reader reader,
            final boolean closeReader) {
        return ExceptionalIterator.of(new Try.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(reader);
                            }
                        }
                    };
                }

                return lazyIter;
            }
        });
    }

    /**
     * It's user's responsibility to close the input resultSet after the stream is finished.
     * 
     * @param resultSet
     * @return
     */
    public static ExceptionalStream rows(final ResultSet resultSet) {
        return rows(Object[].class, resultSet);
    }

    /**
     * 
     * @param resultSet
     * @param closeResultSet
     * @return
     * @deprecated
     */
    @Deprecated
    static ExceptionalStream rows(final ResultSet resultSet, final boolean closeResultSet) {
        return rows(Object[].class, resultSet, closeResultSet);
    }

    /**
     * It's user's responsibility to close the input resultSet after the stream is finished.
     * 
     * @param targetClass Array/List/Map or Entity with getter/setter methods.
     * @param resultSet
     * @return
     */
    public static  ExceptionalStream rows(final Class targetClass, final ResultSet resultSet) {
        N.checkArgNotNull(targetClass, "targetClass");
        N.checkArgNotNull(resultSet, "resultSet");

        return rows(resultSet, JdbcUtil.BiRowMapper.to(targetClass));
    }

    /**
     * 
     * @param targetClass Array/List/Map or Entity with getter/setter methods.
     * @param resultSet
     * @param closeResultSet
     * @return
     * @deprecated
     */
    @Deprecated
    static  ExceptionalStream rows(final Class targetClass, final ResultSet resultSet, final boolean closeResultSet) {
        N.checkArgNotNull(targetClass, "targetClass");
        N.checkArgNotNull(resultSet, "resultSet");

        if (closeResultSet) {
            return rows(targetClass, resultSet).onClose(new Try.Runnable() {
                @Override
                public void run() throws SQLException {
                    JdbcUtil.closeQuietly(resultSet);
                }
            });
        } else {
            return rows(targetClass, resultSet);
        }
    }

    /**
     * It's user's responsibility to close the input resultSet after the stream is finished.
     * 
     * @param resultSet
     * @param rowMapper
     * @return
     */
    public static  ExceptionalStream rows(final ResultSet resultSet, final JdbcUtil.RowMapper rowMapper) {
        N.checkArgNotNull(resultSet, "resultSet");
        N.checkArgNotNull(rowMapper, "rowMapper");

        final ExceptionalIterator iter = new ExceptionalIterator() {
            private boolean hasNext;

            @Override
            public boolean hasNext() throws SQLException {
                if (hasNext == false) {
                    hasNext = resultSet.next();
                }

                return hasNext;
            }

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

                hasNext = false;

                return rowMapper.apply(resultSet);
            }

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

                final long m = hasNext ? n - 1 : n;

                JdbcUtil.skip(resultSet, m);

                hasNext = false;
            }
        };

        return newStream(iter);
    }

    /**
     * It's user's responsibility to close the input resultSet after the stream is finished.
     * 
     * @param resultSet
     * @param rowMapper
     * @return
     */
    public static  ExceptionalStream rows(final ResultSet resultSet, final JdbcUtil.BiRowMapper rowMapper) {
        N.checkArgNotNull(resultSet, "resultSet");
        N.checkArgNotNull(rowMapper, "rowMapper");

        final ExceptionalIterator iter = new ExceptionalIterator() {
            private List columnLabels = null;
            private boolean hasNext;

            @Override
            public boolean hasNext() throws SQLException {
                if (hasNext == false) {
                    hasNext = resultSet.next();
                }

                return hasNext;
            }

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

                hasNext = false;

                if (columnLabels == null) {
                    columnLabels = JdbcUtil.getColumnLabelList(resultSet);
                }

                return rowMapper.apply(resultSet, columnLabels);
            }

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

                final long m = hasNext ? n - 1 : n;

                JdbcUtil.skip(resultSet, m);

                hasNext = false;
            }

            @Override
            public long count() throws SQLException {
                long cnt = 0;

                while (resultSet.next()) {
                    cnt++;
                }

                return cnt;
            }
        };

        return newStream(iter);
    }

    /**
     * It's user's responsibility to close the input resultSet after the stream is finished.
     * 
     * @param resultSet
     * @param columnIndex starts from 0, not 1.
     * @return
     */
    public static  ExceptionalStream rows(final ResultSet resultSet, final int columnIndex) {
        N.checkArgNotNull(resultSet, "resultSet");
        N.checkArgNotNegative(columnIndex, "columnIndex");

        final ExceptionalIterator iter = new ExceptionalIterator() {
            private final int newColumnIndex = columnIndex + 1;
            private boolean hasNext = false;

            @Override
            public boolean hasNext() throws SQLException {
                if (hasNext == false) {
                    hasNext = resultSet.next();
                }

                return hasNext;
            }

            @Override
            public T next() throws SQLException {
                if (!hasNext()) {
                    throw new NoSuchElementException("No more rows");
                }

                final T next = (T) JdbcUtil.getColumnValue(resultSet, newColumnIndex);
                hasNext = false;
                return next;
            }

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

                final long m = hasNext ? n - 1 : n;

                JdbcUtil.skip(resultSet, m);

                hasNext = false;
            }
        };

        return newStream(iter);
    }

    /**
     * 
     * @param resultSet
     * @param columnIndex starts from 0, not 1.
     * @param closeResultSet
     * @return
     * @deprecated
     */
    @Deprecated
    static  ExceptionalStream rows(final ResultSet resultSet, final int columnIndex, final boolean closeResultSet) {
        N.checkArgNotNull(resultSet, "resultSet");
        N.checkArgNotNegative(columnIndex, "columnIndex");

        if (closeResultSet) {
            return (ExceptionalStream) rows(resultSet, columnIndex).onClose(new Try.Runnable() {
                @Override
                public void run() throws SQLException {
                    JdbcUtil.closeQuietly(resultSet);
                }
            });
        } else {
            return rows(resultSet, columnIndex);
        }
    }

    /**
     * It's user's responsibility to close the input resultSet after the stream is finished.
     * 
     * @param resultSet
     * @param columnName
     * @return
     * @throws UncheckedSQLException
     */
    public static  ExceptionalStream rows(final ResultSet resultSet, final String columnName) throws UncheckedSQLException {
        N.checkArgNotNull(resultSet, "resultSet");
        N.checkArgNotNullOrEmpty(columnName, "columnName");

        final ExceptionalIterator iter = new ExceptionalIterator() {
            private int columnIndex = -1;
            private boolean hasNext = false;

            @Override
            public boolean hasNext() throws SQLException {
                if (hasNext == false) {
                    hasNext = resultSet.next();
                }

                return hasNext;
            }

            @Override
            public T next() throws SQLException {
                if (!hasNext()) {
                    throw new NoSuchElementException("No more rows");
                }

                columnIndex = columnIndex == -1 ? getColumnIndex(resultSet, columnName) : columnIndex;

                final T next = (T) JdbcUtil.getColumnValue(resultSet, columnIndex);
                hasNext = false;
                return next;
            }

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

                final long m = hasNext ? n - 1 : n;

                JdbcUtil.skip(resultSet, m);

                hasNext = false;
            }
        };

        return newStream(iter);
    }

    /**
     * 
     * @param resultSet
     * @param columnName
     * @param closeResultSet
     * @return
     * @deprecated
     * @throws UncheckedSQLException
     */
    @Deprecated
    static  ExceptionalStream rows(final ResultSet resultSet, final String columnName, final boolean closeResultSet)
            throws UncheckedSQLException {
        N.checkArgNotNull(resultSet, "resultSet");
        N.checkArgNotNullOrEmpty(columnName, "columnName");

        if (closeResultSet) {
            return (ExceptionalStream) rows(resultSet, columnName).onClose(new Try.Runnable() {
                @Override
                public void run() throws SQLException {
                    JdbcUtil.closeQuietly(resultSet);
                }
            });
        } else {
            return rows(resultSet, columnName);
        }
    }

    private static int getColumnIndex(final ResultSet resultSet, final String columnName) throws UncheckedSQLException {
        int columnIndex = -1;

        try {
            final ResultSetMetaData rsmd = resultSet.getMetaData();
            final int columnCount = rsmd.getColumnCount();

            for (int i = 1; i <= columnCount; i++) {
                if (JdbcUtil.getColumnLabel(rsmd, i).equals(columnName)) {
                    columnIndex = i - 1;
                    break;
                }
            }
        } catch (SQLException e) {
            throw new UncheckedSQLException(e);
        }

        N.checkArgument(columnIndex >= 0, "No column found by name %s", columnName);

        return columnIndex;
    }

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

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

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

        final Deque> closeHandlers = new ArrayDeque<>();

        for (ExceptionalStream e : c) {
            if (N.notNullOrEmpty(e.closeHandlers)) {
                closeHandlers.addAll(e.closeHandlers);
            }
        }

        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();
            }
        }, closeHandlers);
    }

    public ExceptionalStream filter(final Try.Predicate predicate) {
        checkArgNotNull(predicate, "predicate");

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

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

                    if (predicate.test(next)) {
                        break;
                    }
                }

                return next != NONE;
            }

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

                final T result = next;
                next = NONE;
                return result;
            }
        }, sorted, comparator, closeHandlers);
    }

    public ExceptionalStream takeWhile(final Try.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);
    }

    public ExceptionalStream dropWhile(final Try.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.
     * 
     * @param occurrencesFilter
     * @return
     */
    public ExceptionalStream distinct() {
        final Set set = new HashSet<>();

        return filter(new Try.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 Try.Function keyMapper) {
        checkArgNotNull(keyMapper, "keyMapper");

        final Set set = new HashSet<>();

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

    public  ExceptionalStream map(final Try.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);
    }

    public  ExceptionalStream flatMap(final Try.Function, ? extends E> mapper) {
        checkArgNotNull(mapper, "mapper");

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

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

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

                    if (N.notNullOrEmpty(s.closeHandlers)) {
                        closeHandle = new Try.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 Try.Runnable tmp = closeHandle;
                    closeHandle = null;
                    tmp.run();
                }
            }
        };

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

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

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

        return newStream(iter, newCloseHandlers);
    }

    public  ExceptionalStream flattMap(final Try.Function, ? extends E> mapper) {
        checkArgNotNull(mapper, "mapper");

        return flatMap(new Try.Function, E>() {
            @Override
            public ExceptionalStream apply(T t) throws E {
                return ExceptionalStream.of(mapper.apply(t));
            }
        });
    }

    public  ExceptionalStream slidingMap(Try.BiFunction mapper) {
        return slidingMap(mapper, 1);
    }

    public  ExceptionalStream slidingMap(Try.BiFunction mapper, int increment) {
        return slidingMap(mapper, increment, false);
    }

    public  ExceptionalStream slidingMap(final Try.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);
    }

    public  ExceptionalStream slidingMap(Try.TriFunction mapper) {
        return slidingMap(mapper, 1);
    }

    public  ExceptionalStream slidingMap(Try.TriFunction mapper, int increment) {
        return slidingMap(mapper, increment, false);
    }

    public  ExceptionalStream slidingMap(final Try.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 keyMapper
     * @return
     */
    public  ExceptionalStream>, E> groupBy(final Try.Function keyMapper) {
        return groupBy(keyMapper, Suppliers.> ofMap());
    }

    /**
     * 
     * @param keyMapper
     * @param mapFactory
     * @return
     */
    public  ExceptionalStream>, E> groupBy(final Try.Function keyMapper,
            final Supplier>> mapFactory) {
        return groupBy(keyMapper, Fn.FN. identity(), mapFactory);
    }

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

    /**
     * 
     * @param keyMapper
     * @param valueMapper
     * @param mapFactory
     * @return
     * @see Collectors#toMultimap(Function, Function, Supplier)
     */
    public  ExceptionalStream>, E> groupBy(final Try.Function keyMapper,
            final Try.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);

    }

    public  ExceptionalStream, E> groupBy(final Try.Function keyMapper,
            final Try.Function valueMapper, Try.BinaryOperator mergeFunction) {
        return groupBy(keyMapper, valueMapper, mergeFunction, Suppliers. ofMap());
    }

    /**
     * 
     * @param keyMapper
     * @param valueMapper
     * @param mergeFunction
     * @param mapFactory
     * @return
     * @see {@link Fn.FN#throwingMerger()}
     * @see {@link Fn.FN#replacingMerger()}
     * @see {@link Fn.FN#ignoringMerger()}
     */
    public  ExceptionalStream, E> groupBy(final Try.Function keyMapper,
            final Try.Function valueMapper, final Try.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);
    }

    public  ExceptionalStream, E> groupBy(final Try.Function keyMapper,
            final Collector downstream) throws E {
        return groupBy(keyMapper, downstream, Suppliers. ofMap());
    }

    public > ExceptionalStream, E> groupBy(final Try.Function keyMapper,
            final Collector downstream, final Supplier mapFactory) throws E {
        return groupBy(keyMapper, Fn.FN. identity(), downstream, mapFactory);
    }

    public > ExceptionalStream, E> groupBy(final Try.Function keyMapper,
            final Try.Function valueMapper, final Collector downstream) throws E {
        return groupBy(keyMapper, valueMapper, downstream, Suppliers. ofMap());
    }

    public > ExceptionalStream, E> groupBy(final Try.Function keyMapper,
            final Try.Function valueMapper, final Collector downstream,
            final Supplier mapFactory) throws E {
        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);
    }

    public ExceptionalStream, E> collapse(final Try.BiPredicate collapsible) {
        checkArgNotNull(collapsible, "collapsible");

        final ExceptionalIterator iter = elements;

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

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

            @Override
            public Stream next() throws E {
                if (hasNext == false) {
                    next = iter.next();
                }

                final List c = new ArrayList<>();
                c.add(next);

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

                return Stream.of(c);
            }
        }, closeHandlers);
    }

    public > ExceptionalStream collapse(final Try.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 Try.BiPredicate collapsible, final Try.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); } public ExceptionalStream collapse(final Try.BiPredicate collapsible, final U init, final Try.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); } public ExceptionalStream collapse(final Try.BiPredicate collapsible, final Try.Supplier supplier, final Try.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); } public ExceptionalStream collapse(final Try.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); } public ExceptionalStream scan(final Try.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); } public ExceptionalStream scan(final U init, final Try.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); } public ExceptionalStream scan(final U init, final Try.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); } public ExceptionalStream prepend(ExceptionalStream s) { return concat(s, this); } public ExceptionalStream append(ExceptionalStream s) { return concat(this, s); } public ExceptionalStream appendIfEmpty(Supplier> supplier) throws E { if (elements.hasNext() == false) { return append(supplier.get()); } else { return this; } } public ExceptionalStream peek(final Try.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); } public ExceptionalStream, E> split(final int chunkSize) { return splitToList(chunkSize).map(new Try.Function, Stream, E>() { @Override public Stream apply(List t) { return Stream.of(t); } }); } /** * 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 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 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); } public ExceptionalStream, E> sliding(final int windowSize, final int increment) { return slidingToList(windowSize, increment).map(new Try.Function, Stream, E>() { @Override public Stream apply(List t) { return Stream.of(t); } }); } public ExceptionalStream, E> slidingToList(final int windowSize, final int increment) { return sliding(windowSize, increment, Factory. ofList()); } public ExceptionalStream, E> slidingToSet(final int windowSize, final int increment) { return sliding(windowSize, increment, Factory. ofSet()); } 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); } 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); } 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); } 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); } 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 sorted() { return sorted(Comparators.NATURAL_ORDER); } public ExceptionalStream reverseSorted() { return sorted(Comparators.REVERSED_ORDER); } public ExceptionalStream sorted(final Comparator comparator) { final Comparator cmp = comparator == null ? Comparators.NATURAL_ORDER : comparator; if (sorted && cmp == this.comparator) { return this; } return lazyLoad(new Function() { @Override public Object[] apply(final Object[] a) { N.sort((T[]) a, cmp); return a; } }, true, cmp); } @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); } 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 void forEach(Try.Consumer action) throws E { checkArgNotNull(action, "action"); assertNotClosed(); try { while (elements.hasNext()) { action.accept(elements.next()); } } finally { close(); } } public void forEach(final Try.Consumer action, final Try.Runnable onComplete) throws E, E2 { checkArgNotNull(action, "action"); checkArgNotNull(onComplete, "onComplete"); assertNotClosed(); try { while (elements.hasNext()) { action.accept(elements.next()); } onComplete.run(); } finally { close(); } } public void forEach(final Try.Function, E> flatMapper, final Try.BiConsumer action) throws E, E2 { 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(); } } public void forEach(final Try.Function, E> flatMapper, final Try.Function, E2> flatMapper2, final Try.TriConsumer action) throws E, E2, E3 { 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(); } } public void forEachPair(final Try.BiConsumer action) throws E { forEachPair(action, 1); } public void forEachPair(final Try.BiConsumer action, final int increment) throws E { 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(); } } public void forEachTriple(final Try.TriConsumer action) throws E { forEachTriple(action, 1); } public void forEachTriple(final Try.TriConsumer action, final int increment) throws E { 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(); } } 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(); } } @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(); } } 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(); } } @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(); } } public boolean anyMatch(final Try.Predicate predicate) throws E { checkArgNotNull(predicate, "predicate"); assertNotClosed(); try { while (elements.hasNext()) { if (predicate.test(elements.next())) { return true; } } return false; } finally { close(); } } public boolean allMatch(final Try.Predicate predicate) throws E { checkArgNotNull(predicate, "predicate"); assertNotClosed(); try { while (elements.hasNext()) { if (predicate.test(elements.next()) == false) { return false; } } return true; } finally { close(); } } public boolean noneMatch(final Try.Predicate predicate) throws E { checkArgNotNull(predicate, "predicate"); assertNotClosed(); try { while (elements.hasNext()) { if (predicate.test(elements.next())) { return false; } } return true; } finally { close(); } } public Optional findFirst(final Try.Predicate predicate) throws E { checkArgNotNull(predicate, "predicate"); assertNotClosed(); try { while (elements.hasNext()) { T e = elements.next(); if (predicate.test(e)) { return Optional.of(e); } } return (Optional) Optional.empty(); } finally { close(); } } public Optional findLast(final Try.Predicate predicate) throws E { checkArgNotNull(predicate, "predicate"); assertNotClosed(); try { if (elements.hasNext() == false) { return (Optional) Optional.empty(); } boolean hasResult = false; T e = null; T result = null; while (elements.hasNext()) { e = elements.next(); if (predicate.test(e)) { result = e; hasResult = true; } } return hasResult ? Optional.of(result) : (Optional) Optional.empty(); } finally { close(); } } public Optional first() throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return Optional.empty(); } return Optional.of(elements.next()); } finally { close(); } } 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(); } } public Object[] toArray() throws E { assertNotClosed(); try { return toList().toArray(); } finally { close(); } } 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(); } } public List toList() throws E { assertNotClosed(); try { final List result = new ArrayList<>(); while (elements.hasNext()) { result.add(elements.next()); } return result; } finally { close(); } } public Set toSet() throws E { assertNotClosed(); try { final Set result = new HashSet<>(); while (elements.hasNext()) { result.add(elements.next()); } return result; } finally { close(); } } 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 keyMapper * @param valueMapper * @return * @throws E * @throws IllegalStateException if there are duplicated keys. * @see {@link Fn.FN#throwingMerger()} * @see {@link Fn.FN#replacingMerger()} * @see {@link Fn.FN#ignoringMerger()} */ public Map toMap(final Try.Function keyMapper, final Try.Function valueMapper) throws E, IllegalStateException { return toMap(keyMapper, valueMapper, Suppliers. ofMap()); } /** * * @param keyMapper * @param valueMapper * @param mapFactory * @return * @throws E * @throws IllegalStateException if there are duplicated keys. * @see {@link Fn.FN#throwingMerger()} * @see {@link Fn.FN#replacingMerger()} * @see {@link Fn.FN#ignoringMerger()} */ public > M toMap(final Try.Function keyMapper, final Try.Function valueMapper, final Supplier mapFactory) throws E, IllegalStateException { return toMap(keyMapper, valueMapper, Fn.FN. throwingMerger(), mapFactory); } /** * * @param keyMapper * @param valueMapper * @param mergeFunction * @return * @throws E * @see {@link Fn.FN#throwingMerger()} * @see {@link Fn.FN#replacingMerger()} * @see {@link Fn.FN#ignoringMerger()} */ public Map toMap(final Try.Function keyMapper, final Try.Function valueMapper, final Try.BinaryOperator mergeFunction) throws E { return toMap(keyMapper, valueMapper, mergeFunction, Suppliers. ofMap()); } /** * * @param keyMapper * @param valueMapper * @param mergeFunction * @param mapFactory * @return * @throws E * @see {@link Fn.FN#throwingMerger()} * @see {@link Fn.FN#replacingMerger()} * @see {@link Fn.FN#ignoringMerger()} */ public > M toMap(final Try.Function keyMapper, final Try.Function valueMapper, final Try.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 keyMapper * @param downstream * @return * @throws E */ public Map toMap(final Try.Function keyMapper, final Collector downstream) throws E { return toMap(keyMapper, downstream, Suppliers. ofMap()); } /** * * @param keyMapper * @param downstream * @param mapFactory * @return * @throws E */ public > M toMap(final Try.Function keyMapper, final Collector downstream, final Supplier mapFactory) throws E { return toMap(keyMapper, Fn.FN. identity(), downstream, mapFactory); } /** * * @param keyMapper * @param valueMapper * @param downstream * @return * @throws E */ public Map toMap(final Try.Function keyMapper, final Try.Function valueMapper, final Collector downstream) throws E { return toMap(keyMapper, valueMapper, downstream, Suppliers. ofMap()); } /** * * @param keyMapper * @param valueMapper * @param downstream * @param mapFactory * @return * @throws E */ public > M toMap(final Try.Function keyMapper, final Try.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 keyMapper * @return * @see Collectors#groupingBy(Function) */ public Map> groupTo(Try.Function keyMapper) throws E { return groupTo(keyMapper, Suppliers.> ofMap()); } /** * * @param keyMapper * @param mapFactory * @return * @see Collectors#groupingBy(Function, Supplier) */ public >> M groupTo(final Try.Function keyMapper, final Supplier mapFactory) throws E { final Try.Function valueMapper = Fn.FN.identity(); return groupTo(keyMapper, valueMapper, mapFactory); } public Map> groupTo(Try.Function keyMapper, Try.Function valueMapper) throws E { return groupTo(keyMapper, valueMapper, Suppliers.> ofMap()); } /** * * @param keyMapper * @param valueMapper * @param mapFactory * @return * @see Collectors#toMultimap(Function, Function, Supplier) */ public >> M groupTo(Try.Function keyMapper, Try.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 long count() throws E { assertNotClosed(); try { return elements.count(); } finally { close(); } } /** * * @return * @throws DuplicatedResultException if there are more than one elements. * @throws 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(); } } public OptionalInt sumInt(Try.ToIntFunction func) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return OptionalInt.empty(); } long sum = 0; while (elements.hasNext()) { sum += func.applyAsInt(elements.next()); } return OptionalInt.of(N.toIntExact(sum)); } finally { close(); } } public OptionalLong sumLong(Try.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(); } } public OptionalDouble sumDouble(Try.ToDoubleFunction func) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return OptionalDouble.empty(); } final List list = new ArrayList<>(); while (elements.hasNext()) { list.add(func.applyAsDouble(elements.next())); } return OptionalDouble.of(N.sumDouble(list)); } finally { close(); } } public OptionalDouble averageInt(Try.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(); } } public OptionalDouble averageLong(Try.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(); } } public OptionalDouble averageDouble(Try.ToDoubleFunction func) throws E { assertNotClosed(); try { if (elements.hasNext() == false) { return OptionalDouble.empty(); } final List list = new ArrayList<>(); while (elements.hasNext()) { list.add(func.applyAsDouble(elements.next())); } return N.averageLong(list); } finally { close(); } } public Optional reduce(Try.BinaryOperator accumulator) throws E { 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(); } } public U reduce(final U identity, final Try.BiFunction accumulator) throws E { checkArgNotNull(accumulator, "accumulator"); assertNotClosed(); try { U result = identity; while (elements.hasNext()) { result = accumulator.apply(result, elements.next()); } return result; } finally { close(); } } public R collect(Try.Supplier supplier, final Try.BiConsumer accumulator) throws E { 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(); } } public RR collect(Try.Supplier supplier, final Try.BiConsumer accumulator, final Try.Function finisher) throws E { 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(); } } 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(); } } public R collect(java.util.stream.Collector collector) throws E { checkArgNotNull(collector, "collector"); assertNotClosed(); try { final A container = collector.supplier().get(); final java.util.function.BiConsumer accumulator = collector.accumulator(); while (elements.hasNext()) { accumulator.accept(container, elements.next()); } return collector.finisher().apply(container); } finally { close(); } } public RR collectAndThen(final Collector collector, final Try.Function func) throws E { checkArgNotNull(collector, "collector"); checkArgNotNull(func, "func"); assertNotClosed(); return func.apply(collect(collector)); } public RR collectAndThen(final java.util.stream.Collector collector, final Try.Function func) throws E { checkArgNotNull(collector, "collector"); checkArgNotNull(func, "func"); assertNotClosed(); return func.apply(collect(collector)); } public Stream unchecked() { if (N.isNullOrEmpty(this.closeHandlers)) { return Stream.of(new ObjIteratorEx() { @Override public boolean hasNext() { try { return elements.hasNext(); } catch (Exception e) { throw N.toRuntimeException(e); } } @Override public T next() { try { return elements.next(); } catch (Exception e) { throw N.toRuntimeException(e); } } @Override public void skip(long n) { N.checkArgNotNegative(n, "n"); try { elements.skip(n); } catch (Exception e) { throw N.toRuntimeException(e); } } @Override public long count() { try { return elements.count(); } catch (Exception e) { throw N.toRuntimeException(e); } } }); } else { return Stream.of(new ObjIteratorEx() { @Override public boolean hasNext() { try { return elements.hasNext(); } catch (Exception e) { throw N.toRuntimeException(e); } } @Override public T next() { try { return elements.next(); } catch (Exception e) { throw N.toRuntimeException(e); } } @Override public void skip(long n) { N.checkArgNotNegative(n, "n"); try { elements.skip(n); } catch (Exception e) { throw N.toRuntimeException(e); } } @Override public long count() { try { return elements.count(); } catch (Exception e) { throw N.toRuntimeException(e); } } }).onClose(new Runnable() { @Override public void run() { try { ExceptionalStream.this.close(); } catch (Exception e) { throw N.toRuntimeException(e); } } }); } } // public ExceptionalStream __(final Class targetExceptionType) { // checkArgNotNull(targetExceptionType, "targetExceptionType"); // // final Constructor msgCauseConstructor = ClassUtil.getDeclaredConstructor(targetExceptionType, String.class, Throwable.class); // final Constructor causeOnlyConstructor = ClassUtil.getDeclaredConstructor(targetExceptionType, Throwable.class); // // checkArgument(msgCauseConstructor != null || causeOnlyConstructor != null, // "No constructor found with parameters: (String.class, Throwable.class), or (Throwable.class)"); // // final Function convertE = msgCauseConstructor != null ? new Function() { // @Override // public E2 apply(Exception e) { // return ClassUtil.invokeConstructor(msgCauseConstructor, e.getMessage(), e); // } // } : new Function() { // @Override // public E2 apply(Exception e) { // return ClassUtil.invokeConstructor(causeOnlyConstructor, e); // } // }; // // Deque> newCloseHandlers = null; // // if (closeHandlers != null) { // newCloseHandlers = new ArrayDeque<>(1); // newCloseHandlers.add(new Try.Runnable() { // @Override // public void run() throws E2 { // try { // close(); // } catch (Exception e) { // throw convertE.apply(e); // } // } // }); // } // // return newStream(new ExceptionalIterator() { // private ExceptionalIterator iter = null; // private boolean initialized = false; // // @Override // public boolean hasNext() throws E2 { // if (initialized == false) { // init(); // } // // try { // return iter.hasNext(); // } catch (Exception e) { // throw convertE.apply(e); // } // } // // @Override // public T next() throws E2 { // if (initialized == false) { // init(); // } // // try { // return iter.next(); // } catch (Exception e) { // throw convertE.apply(e); // } // } // // @Override // public void skip(long n) throws E2 { // checkArgNotNegative(n, "n"); // // if (initialized == false) { // init(); // } // // try { // iter.skip(n); // } catch (Exception e) { // throw convertE.apply(e); // } // } // // @Override // public long count() throws E2 { // if (initialized == false) { // init(); // } // // try { // return iter.count(); // } catch (Exception e) { // throw convertE.apply(e); // } // } // // private void init() { // if (initialized == false) { // initialized = true; // // iter = ExceptionalStream.this.elements; // // } // } // }, sorted, comparator, newCloseHandlers); // } public R __(Try.Function, R, ? extends E> transfer) throws E { checkArgNotNull(transfer, "transfer"); return transfer.apply(this); } /** * * @param action a terminal operation should be called. * @return */ @Beta public ContinuableFuture asyncRun(final Try.Consumer, E> action) { checkArgNotNull(action, "action"); return ContinuableFuture.run(new Try.Runnable() { @Override public void run() throws E { action.accept(ExceptionalStream.this); } }); } /** * * @param action a terminal operation should be called. * @param executor * @return */ @Beta public ContinuableFuture asyncRun(final Try.Consumer, E> action, final Executor executor) { checkArgNotNull(action, "action"); checkArgNotNull(executor, "executor"); return ContinuableFuture.run(new Try.Runnable() { @Override public void run() throws E { action.accept(ExceptionalStream.this); } }, executor); } /** * * @param action a terminal operation should be called. * @return */ @Beta public ContinuableFuture asyncCall(final Try.Function, R, E> action) { checkArgNotNull(action, "action"); return ContinuableFuture.call(new Try.Callable() { @Override public R call() throws E { return action.apply(ExceptionalStream.this); } }); } /** * * @param action a terminal operation should be called. * @param executor * @return */ @Beta public ContinuableFuture asyncCall(final Try.Function, R, E> action, final Executor executor) { checkArgNotNull(action, "action"); checkArgNotNull(executor, "executor"); return ContinuableFuture.call(new Try.Callable() { @Override public R call() throws E { return action.apply(ExceptionalStream.this); } }, executor); } public ExceptionalStream onClose(final Try.Runnable closeHandler) { checkArgNotNull(closeHandler, "closeHandler"); final Deque> newCloseHandlers = new ArrayDeque<>(N.size(closeHandlers) + 1); newCloseHandlers.add(new Try.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); } @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 (Try.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; } } } void assertNotClosed() { if (isClosed) { throw new IllegalStateException("This stream has been closed"); } } 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; } 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; } 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; } private void checkArgument(boolean b, String errorMessage) { if (!b) { try { N.checkArgument(b, errorMessage); } finally { try { close(); } catch (Exception e) { throw N.toRuntimeException(e); } } } } 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); } } } } static ExceptionalStream newStream(final ExceptionalIterator iter) { return new ExceptionalStream<>(iter, null); } static ExceptionalStream newStream(final ExceptionalIterator iter, final Deque> closeHandlers) { return new ExceptionalStream<>(iter, closeHandlers); } static ExceptionalStream newStream(final ExceptionalIterator iter, final boolean sorted, final Comparator comparator, final Deque> closeHandlers) { return new ExceptionalStream<>(iter, sorted, comparator, closeHandlers); } static Object hashKey(Object obj) { return obj == null || obj.getClass().isArray() == false ? obj : Wrapper.of(obj); } static boolean isSameComparator(Comparator a, Comparator b) { return a == b || (a == null && b == Comparators.NATURAL_ORDER) || (b == null && a == Comparators.NATURAL_ORDER); } public static final class StreamE extends ExceptionalStream { StreamE(ExceptionalIterator iter, boolean sorted, Comparator comparator, Deque> closeHandlers) { super(iter, sorted, comparator, closeHandlers); } } static abstract class ExceptionalIterator { @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(); } }; /** * Lazy evaluation. * * @param iteratorSupplier * @return */ public static ExceptionalIterator of(final Try.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 arraySupplier * @return */ public static ExceptionalIterator oF(final Try.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); } } }; } public abstract boolean hasNext() throws E; public abstract T next() throws E; public void skip(long n) throws E { N.checkArgNotNegative(n, "n"); while (n-- > 0 && hasNext()) { next(); } } public long count() throws E { long result = 0; while (hasNext()) { next(); result++; } return result; } public void close() throws E { // Nothing to do by default. } } }