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

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

There is a newer version: 1.2.9
Show newest version
/*
 * Copyright (c) 2017, 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.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
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 com.landawn.abacus.util.function.BiFunction;
import com.landawn.abacus.util.function.BooleanSupplier;
import com.landawn.abacus.util.function.Function;
import com.landawn.abacus.util.function.Predicate;
import com.landawn.abacus.util.function.Supplier;
import com.landawn.abacus.util.function.TriFunction;

/**
 * 
 * 
 * @since 0.9
 * 
 * @author Haiyang Li
 */
public final class Iterators {
    private Iterators() {
        // singleton.
    }

    public static  List toList(final Iterator iter) {
        if (iter == null) {
            return new ArrayList<>();
        }

        final List result = new ArrayList<>();

        while (iter.hasNext()) {
            result.add(iter.next());
        }

        return result;
    }

    public static  Set toSet(final Iterator iter) {
        if (iter == null) {
            return new HashSet<>();
        }

        final Set result = new HashSet<>();

        while (iter.hasNext()) {
            result.add(iter.next());
        }

        return result;
    }

    public static > C toCollection(final Iterator iter, final Supplier collectionFactory) {
        final C c = collectionFactory.get();

        if (iter == null) {
            return c;
        }

        while (iter.hasNext()) {
            c.add(iter.next());
        }

        return c;
    }

    public static  Map toMap(final Iterator iter, final Try.Function keyExtractor) throws E {
        N.requireNonNull(keyExtractor);

        if (iter == null) {
            return new HashMap<>();
        }

        final Map result = new HashMap<>();
        T next = null;

        while (iter.hasNext()) {
            next = iter.next();
            result.put(keyExtractor.apply(next), next);
        }

        return result;
    }

    public static  Map toMap(final Iterator iter,
            final Try.Function keyExtractor, final Try.Function valueExtractor) throws E, E2 {
        N.requireNonNull(keyExtractor);
        N.requireNonNull(valueExtractor);

        if (iter == null) {
            return new HashMap<>();
        }

        final Map result = new HashMap<>();
        T next = null;

        while (iter.hasNext()) {
            next = iter.next();
            result.put(keyExtractor.apply(next), valueExtractor.apply(next));
        }

        return result;
    }

    public static , E extends Exception, E2 extends Exception> M toMap(final Iterator iter,
            final Try.Function keyExtractor, final Try.Function valueExtractor, final Supplier mapSupplier)
            throws E, E2 {
        N.requireNonNull(keyExtractor);
        N.requireNonNull(valueExtractor);

        if (iter == null) {
            return mapSupplier.get();
        }

        final M result = mapSupplier.get();
        T next = null;

        while (iter.hasNext()) {
            next = iter.next();
            result.put(keyExtractor.apply(next), valueExtractor.apply(next));
        }

        return result;
    }

    public static  ObjIterator repeat(final T e, final int n) {
        N.checkArgument(n >= 0, "'n' can't be negative: %s", n);

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

        return new ObjIterator() {
            private int cnt = n;

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

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

                cnt--;
                return e;
            }
        };
    };

    public static  ObjIterator repeatt(final Collection c, final int n) {
        N.checkArgument(n >= 0, "'n' can't be negative: %s", n);

        if (n == 0 || N.isNullOrEmpty(c)) {
            return ObjIterator.empty();
        }

        return new ObjIterator() {
            private Iterator iter = null;
            private int cnt = n;

            @Override
            public boolean hasNext() {
                return cnt > 0 || (iter != null && iter.hasNext());
            }

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

                if (iter == null || iter.hasNext() == false) {
                    iter = c.iterator();
                    cnt--;
                }

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

    public static  ObjIterator repeatToSize(final Collection c, final int size) {
        N.checkArgument(size >= 0, "'size' can't be negative: %s", size);
        N.checkArgument(size == 0 || N.notNullOrEmpty(c), "Collection can't be empty or null when size > 0");

        if (N.isNullOrEmpty(c) || size == 0) {
            return ObjIterator.empty();
        }

        return new ObjIterator() {
            private Iterator iter = null;
            private int cnt = size;

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

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

                if (iter == null || iter.hasNext() == false) {
                    iter = c.iterator();
                }

                cnt--;

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

    public static  ObjIterator nRepeat(final Collection c, final int n) {
        N.checkArgument(n >= 0, "'n' can't be negative: %s", n);

        if (n == 0 || N.isNullOrEmpty(c)) {
            return ObjIterator.empty();
        }

        return new ObjIterator() {
            private Iterator iter = null;
            private T next = null;
            private int cnt = n;

            @Override
            public boolean hasNext() {
                return cnt > 0 || (iter != null && iter.hasNext());
            }

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

                if (iter == null) {
                    iter = c.iterator();
                    next = iter.next();
                } else if (cnt <= 0) {
                    next = iter.next();
                    cnt = n;
                }

                cnt--;

                return next;
            }
        };
    };

    public static  ObjIterator nRepeatToSize(final Collection c, final int size) {
        N.checkArgument(size >= 0, "'size' can't be negative: %s", size);
        N.checkArgument(size == 0 || N.notNullOrEmpty(c), "Collection can't be empty or null when size > 0");

        if (N.isNullOrEmpty(c) || size == 0) {
            return ObjIterator.empty();
        }

        return new ObjIterator() {
            private final int n = size / c.size();
            private int mod = size % c.size();

            private Iterator iter = null;
            private T next = null;
            private int cnt = mod-- > 0 ? n + 1 : n;

            @Override
            public boolean hasNext() {
                return cnt > 0 || ((n > 0 || mod > 0) && (iter != null && iter.hasNext()));
            }

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

                if (iter == null) {
                    iter = c.iterator();
                    next = iter.next();
                } else if (cnt <= 0) {
                    next = iter.next();
                    cnt = mod-- > 0 ? n + 1 : n;
                }

                cnt--;

                return next;
            }
        };
    };

    @SafeVarargs
    public static BooleanIterator concat(final boolean[]... a) {
        if (N.isNullOrEmpty(a)) {
            return BooleanIterator.EMPTY;
        }

        return new BooleanIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private boolean[] cur;
            private int cursor = 0;

            @Override
            public boolean hasNext() {
                while ((N.isNullOrEmpty(cur) || cursor >= cur.length) && iter.hasNext()) {
                    cur = iter.next();
                    cursor = 0;
                }

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

            @Override
            public boolean nextBoolean() {
                if ((cur == null || cursor >= cur.length) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur[cursor++];
            }
        };
    }

    @SafeVarargs
    public static ShortIterator concat(final short[]... a) {
        if (N.isNullOrEmpty(a)) {
            return ShortIterator.EMPTY;
        }

        return new ShortIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private short[] cur;
            private int cursor = 0;

            @Override
            public boolean hasNext() {
                while ((N.isNullOrEmpty(cur) || cursor >= cur.length) && iter.hasNext()) {
                    cur = iter.next();
                    cursor = 0;
                }

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

            @Override
            public short nextShort() {
                if ((cur == null || cursor >= cur.length) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur[cursor++];
            }
        };
    }

    @SafeVarargs
    public static ByteIterator concat(final byte[]... a) {
        if (N.isNullOrEmpty(a)) {
            return ByteIterator.EMPTY;
        }

        return new ByteIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private byte[] cur;
            private int cursor = 0;

            @Override
            public boolean hasNext() {
                while ((N.isNullOrEmpty(cur) || cursor >= cur.length) && iter.hasNext()) {
                    cur = iter.next();
                    cursor = 0;
                }

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

            @Override
            public byte nextByte() {
                if ((cur == null || cursor >= cur.length) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur[cursor++];
            }
        };
    }

    @SafeVarargs
    public static IntIterator concat(final int[]... a) {
        if (N.isNullOrEmpty(a)) {
            return IntIterator.EMPTY;
        }

        return new IntIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private int[] cur;
            private int cursor = 0;

            @Override
            public boolean hasNext() {
                while ((N.isNullOrEmpty(cur) || cursor >= cur.length) && iter.hasNext()) {
                    cur = iter.next();
                    cursor = 0;
                }

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

            @Override
            public int nextInt() {
                if ((cur == null || cursor >= cur.length) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur[cursor++];
            }
        };
    }

    @SafeVarargs
    public static LongIterator concat(final long[]... a) {
        if (N.isNullOrEmpty(a)) {
            return LongIterator.EMPTY;
        }

        return new LongIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private long[] cur;
            private int cursor = 0;

            @Override
            public boolean hasNext() {
                while ((N.isNullOrEmpty(cur) || cursor >= cur.length) && iter.hasNext()) {
                    cur = iter.next();
                    cursor = 0;
                }

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

            @Override
            public long nextLong() {
                if ((cur == null || cursor >= cur.length) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur[cursor++];
            }
        };
    }

    @SafeVarargs
    public static FloatIterator concat(final float[]... a) {
        if (N.isNullOrEmpty(a)) {
            return FloatIterator.EMPTY;
        }

        return new FloatIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private float[] cur;
            private int cursor = 0;

            @Override
            public boolean hasNext() {
                while ((N.isNullOrEmpty(cur) || cursor >= cur.length) && iter.hasNext()) {
                    cur = iter.next();
                    cursor = 0;
                }

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

            @Override
            public float nextFloat() {
                if ((cur == null || cursor >= cur.length) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur[cursor++];
            }
        };
    }

    @SafeVarargs
    public static DoubleIterator concat(final double[]... a) {
        if (N.isNullOrEmpty(a)) {
            return DoubleIterator.EMPTY;
        }

        return new DoubleIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private double[] cur;
            private int cursor = 0;

            @Override
            public boolean hasNext() {
                while ((N.isNullOrEmpty(cur) || cursor >= cur.length) && iter.hasNext()) {
                    cur = iter.next();
                    cursor = 0;
                }

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

            @Override
            public double nextDouble() {
                if ((cur == null || cursor >= cur.length) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur[cursor++];
            }
        };
    }

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

        return new BooleanIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private BooleanIterator cur;

            @Override
            public boolean hasNext() {
                while ((cur == null || cur.hasNext() == false) && iter.hasNext()) {
                    cur = iter.next();
                }

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

            @Override
            public boolean nextBoolean() {
                if ((cur == null || cur.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur.nextBoolean();
            }
        };
    }

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

        return new CharIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private CharIterator cur;

            @Override
            public boolean hasNext() {
                while ((cur == null || cur.hasNext() == false) && iter.hasNext()) {
                    cur = iter.next();
                }

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

            @Override
            public char nextChar() {
                if ((cur == null || cur.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur.nextChar();
            }
        };
    }

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

        return new ByteIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private ByteIterator cur;

            @Override
            public boolean hasNext() {
                while ((cur == null || cur.hasNext() == false) && iter.hasNext()) {
                    cur = iter.next();
                }

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

            @Override
            public byte nextByte() {
                if ((cur == null || cur.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur.nextByte();
            }
        };
    }

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

        return new ShortIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private ShortIterator cur;

            @Override
            public boolean hasNext() {
                while ((cur == null || cur.hasNext() == false) && iter.hasNext()) {
                    cur = iter.next();
                }

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

            @Override
            public short nextShort() {
                if ((cur == null || cur.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur.nextShort();
            }
        };
    }

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

        return new IntIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private IntIterator cur;

            @Override
            public boolean hasNext() {
                while ((cur == null || cur.hasNext() == false) && iter.hasNext()) {
                    cur = iter.next();
                }

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

            @Override
            public int nextInt() {
                if ((cur == null || cur.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur.nextInt();
            }
        };
    }

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

        return new LongIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private LongIterator cur;

            @Override
            public boolean hasNext() {
                while ((cur == null || cur.hasNext() == false) && iter.hasNext()) {
                    cur = iter.next();
                }

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

            @Override
            public long nextLong() {
                if ((cur == null || cur.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur.nextLong();
            }
        };
    }

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

        return new FloatIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private FloatIterator cur;

            @Override
            public boolean hasNext() {
                while ((cur == null || cur.hasNext() == false) && iter.hasNext()) {
                    cur = iter.next();
                }

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

            @Override
            public float nextFloat() {
                if ((cur == null || cur.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur.nextFloat();
            }
        };
    }

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

        return new DoubleIterator() {
            private final Iterator iter = Arrays.asList(a).iterator();
            private DoubleIterator cur;

            @Override
            public boolean hasNext() {
                while ((cur == null || cur.hasNext() == false) && iter.hasNext()) {
                    cur = iter.next();
                }

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

            @Override
            public double nextDouble() {
                if ((cur == null || cur.hasNext() == false) && hasNext() == false) {
                    throw new NoSuchElementException();
                }

                return cur.nextDouble();
            }
        };
    }

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

        final List> list = new ArrayList<>(a.length);

        for (T[] e : a) {
            if (N.notNullOrEmpty(e)) {
                list.add(ObjIterator.of(e));
            }
        }

        return concat(list);
    }

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

        final List> list = new ArrayList<>(a.length);

        for (Collection e : a) {
            if (N.notNullOrEmpty(e)) {
                list.add(e.iterator());
            }
        }

        return concat(list);
    }

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

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

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

        return new ObjIterator() {
            private final Iterator> iter = c.iterator();
            private Iterator cur;

            @Override
            public boolean hasNext() {
                while ((cur == null || cur.hasNext() == false) && iter.hasNext()) {
                    cur = iter.next();
                }

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

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

                return cur.next();
            }
        };
    }

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

        return new ObjIterator() {
            private final Iterator> iter = c.iterator();
            private Iterator cur;

            @Override
            public boolean hasNext() {
                while ((cur == null || cur.hasNext() == false) && iter.hasNext()) {
                    final Collection c = iter.next();
                    cur = N.isNullOrEmpty(c) ? null : c.iterator();
                }

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

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

                return cur.next();
            }
        };
    }

    public static  ObjIterator merge(final Collection a, final Collection b,
            final BiFunction nextSelector) {
        final Iterator iterA = N.isNullOrEmpty(a) ? ObjIterator. empty() : (Iterator) a.iterator();
        final Iterator iterB = N.isNullOrEmpty(b) ? ObjIterator. empty() : (Iterator) b.iterator();

        return merge(iterA, iterB, nextSelector);

    }

    public static  ObjIterator merge(final Iterator a, final Iterator b,
            final BiFunction nextSelector) {
        N.requireNonNull(nextSelector);

        return new ObjIterator() {
            private final Iterator iterA = a == null ? ObjIterator. empty() : a;
            private final Iterator iterB = b == null ? ObjIterator. empty() : b;
            private T nextA = null;
            private T nextB = null;
            private boolean hasNextA = false;
            private boolean hasNextB = false;

            @Override
            public boolean hasNext() {
                return hasNextA || hasNextB || iterA.hasNext() || iterB.hasNext();
            }

            @Override
            public T next() {
                if (hasNextA) {
                    if (iterB.hasNext()) {
                        if (nextSelector.apply(nextA, (nextB = iterB.next())) == Nth.FIRST) {
                            hasNextA = false;
                            hasNextB = true;
                            return nextA;
                        } else {
                            return nextB;
                        }
                    } else {
                        hasNextA = false;
                        return nextA;
                    }
                } else if (hasNextB) {
                    if (iterA.hasNext()) {
                        if (nextSelector.apply((nextA = iterA.next()), nextB) == Nth.FIRST) {
                            return nextA;
                        } else {
                            hasNextA = true;
                            hasNextB = false;
                            return nextB;
                        }
                    } else {
                        hasNextB = false;
                        return nextB;
                    }
                } else if (iterA.hasNext()) {
                    if (iterB.hasNext()) {
                        if (nextSelector.apply((nextA = iterA.next()), (nextB = iterB.next())) == Nth.FIRST) {
                            hasNextB = true;
                            return nextA;
                        } else {
                            hasNextA = true;
                            return nextB;
                        }
                    } else {
                        return iterA.next();
                    }
                } else {
                    return iterB.next();
                }
            }
        };
    }

    public static  ObjIterator zip(final Collection a, final Collection b, final BiFunction zipFunction) {
        final Iterator iterA = N.isNullOrEmpty(a) ? ObjIterator. empty() : a.iterator();
        final Iterator iterB = N.isNullOrEmpty(b) ? ObjIterator. empty() : b.iterator();

        return zip(iterA, iterB, zipFunction);
    }

    public static  ObjIterator zip(final Iterator a, final Iterator b, final BiFunction zipFunction) {
        N.requireNonNull(zipFunction);

        return new ObjIterator() {
            private final Iterator iterA = a == null ? ObjIterator. empty() : a;
            private final Iterator iterB = b == null ? ObjIterator. empty() : b;

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

            @Override
            public R next() {
                return zipFunction.apply(iterA.next(), iterB.next());
            }
        };
    }

    public static  ObjIterator zip(final Collection a, final Collection b, final Collection c,
            final TriFunction zipFunction) {
        final Iterator iterA = N.isNullOrEmpty(a) ? ObjIterator. empty() : a.iterator();
        final Iterator iterB = N.isNullOrEmpty(b) ? ObjIterator. empty() : b.iterator();
        final Iterator iterC = N.isNullOrEmpty(c) ? ObjIterator. empty() : c.iterator();

        return zip(iterA, iterB, iterC, zipFunction);
    }

    public static  ObjIterator zip(final Iterator a, final Iterator b, final Iterator c,
            final TriFunction zipFunction) {
        N.requireNonNull(zipFunction);

        return new ObjIterator() {
            private final Iterator iterA = a == null ? ObjIterator. empty() : a;
            private final Iterator iterB = b == null ? ObjIterator. empty() : b;
            private final Iterator iterC = c == null ? ObjIterator. empty() : c;

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

            @Override
            public R next() {
                return zipFunction.apply(iterA.next(), iterB.next(), iterC.next());
            }
        };
    }

    public static  ObjIterator zip(final Collection a, final Collection b, final A valueForNoneA, final B valueForNoneB,
            final BiFunction zipFunction) {
        final Iterator iterA = N.isNullOrEmpty(a) ? ObjIterator. empty() : a.iterator();
        final Iterator iterB = N.isNullOrEmpty(b) ? ObjIterator. empty() : b.iterator();

        return zip(iterA, iterB, valueForNoneA, valueForNoneB, zipFunction);
    }

    public static  ObjIterator zip(final Iterator a, final Iterator b, final A valueForNoneA, final B valueForNoneB,
            final BiFunction zipFunction) {
        N.requireNonNull(zipFunction);

        return new ObjIterator() {
            private final Iterator iterA = a == null ? ObjIterator. empty() : a;
            private final Iterator iterB = b == null ? ObjIterator. empty() : b;

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

            @Override
            public R next() {
                return zipFunction.apply(iterA.hasNext() ? iterA.next() : valueForNoneA, iterB.hasNext() ? iterB.next() : valueForNoneB);
            }
        };
    }

    public static  ObjIterator zip(final Collection a, final Collection b, final Collection c, final A valueForNoneA,
            final B valueForNoneB, final C valueForNoneC, final TriFunction zipFunction) {
        final Iterator iterA = N.isNullOrEmpty(a) ? ObjIterator. empty() : a.iterator();
        final Iterator iterB = N.isNullOrEmpty(b) ? ObjIterator. empty() : b.iterator();
        final Iterator iterC = N.isNullOrEmpty(c) ? ObjIterator. empty() : c.iterator();

        return zip(iterA, iterB, iterC, valueForNoneA, valueForNoneB, valueForNoneC, zipFunction);
    }

    public static  ObjIterator zip(final Iterator a, final Iterator b, final Iterator c, final A valueForNoneA, final B valueForNoneB,
            final C valueForNoneC, final TriFunction zipFunction) {
        return new ObjIterator() {
            private final Iterator iterA = a == null ? ObjIterator. empty() : a;
            private final Iterator iterB = b == null ? ObjIterator. empty() : b;
            private final Iterator iterC = c == null ? ObjIterator. empty() : c;

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

            @Override
            public R next() {
                return zipFunction.apply(iterA.hasNext() ? iterA.next() : valueForNoneA, iterB.hasNext() ? iterB.next() : valueForNoneB,
                        iterC.hasNext() ? iterC.next() : valueForNoneC);
            }
        };
    }

    /**
     * 
     * @param iter
     * @param unzip the second parameter is an output parameter.
     * @return
     */
    public static  Pair, List> unzip(final Iterator iter,
            final Try.BiConsumer, E> unzip) throws E {
        final List l = new ArrayList();
        final List r = new ArrayList();
        final Pair p = new Pair<>();

        if (iter != null) {
            while (iter.hasNext()) {
                unzip.accept(iter.next(), p);

                l.add(p.left);
                r.add(p.right);
            }
        }

        return Pair.of(l, r);
    }

    /**
     * 
     * @param iter
     * @param unzip the second parameter is an output parameter.
     * @param supplier
     * @return
     */
    public static , RC extends Collection, E extends Exception> Pair unzip(final Iterator iter,
            final Try.BiConsumer, E> unzip, final Supplier> supplier) throws E {
        final LC l = (LC) supplier.get();
        final RC r = (RC) supplier.get();

        final Pair p = new Pair<>();

        if (iter != null) {
            while (iter.hasNext()) {
                unzip.accept(iter.next(), p);

                l.add(p.left);
                r.add(p.right);
            }
        }

        return Pair.of(l, r);
    }

    /**
     * 
     * @param iter
     * @param unzip the second parameter is an output parameter.
     * @return
     */
    public static  Triple, List, List> unzipp(final Iterator iter,
            final Try.BiConsumer, E> unzip) throws E {
        final List l = new ArrayList();
        final List m = new ArrayList();
        final List r = new ArrayList();
        final Triple t = new Triple<>();

        if (iter != null) {
            while (iter.hasNext()) {
                unzip.accept(iter.next(), t);

                l.add(t.left);
                m.add(t.middle);
                r.add(t.right);
            }
        }

        return Triple.of(l, m, r);
    }

    /**
     * 
     * @param iter
     * @param unzip the second parameter is an output parameter.
     * @param supplier
     * @return
     */
    public static , MC extends Collection, RC extends Collection, E extends Exception> Triple unzipp(
            final Iterator iter, final Try.BiConsumer, E> unzip, final Supplier> supplier)
            throws E {
        final LC l = (LC) supplier.get();
        final MC m = (MC) supplier.get();
        final RC r = (RC) supplier.get();
        final Triple t = new Triple<>();

        if (iter != null) {
            while (iter.hasNext()) {
                unzip.accept(iter.next(), t);

                l.add(t.left);
                m.add(t.middle);
                r.add(t.right);
            }
        }

        return Triple.of(l, m, r);
    }

    public static  ObjIterator> split(final Iterator iter, final int size) {
        N.checkArgument(size > 0, "'size' must be greater than 0, can't be: %s", size);

        if (iter == null) {
            return ObjIterator.empty();
        }

        return new ObjIterator>() {
            private final Iterator iterator = iter;

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

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

                final List next = new ArrayList<>(size);

                for (int i = 0; i < size && iterator.hasNext(); i++) {
                    next.add(iterator.next());
                }

                return next;
            }
        };
    }

    public static  Nullable first(final Iterator iter) {
        return iter != null && iter.hasNext() ? Nullable.of(iter.next()) : Nullable. empty();
    }

    public static  Optional firstNonNull(final Iterator iter) {
        if (iter == null) {
            return Optional.empty();
        }

        T next = null;

        while (iter.hasNext()) {
            if ((next = iter.next()) != null) {
                return Optional.of(next);
            }
        }

        return Optional.empty();
    }

    public static  Nullable last(final Iterator iter) {
        if (iter == null || iter.hasNext() == false) {
            return Nullable.empty();
        }

        T next = null;

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

        return Nullable.of(next);
    }

    public static  Optional lastNonNull(final Iterator iter) {
        if (iter == null) {
            return Optional.empty();
        }

        T next = null;
        T lastNonNull = null;

        while (iter.hasNext()) {
            if ((next = iter.next()) != null) {
                lastNonNull = next;
            }
        }

        return Optional.ofNullable(lastNonNull);
    }

    public static  ObjIterator skipNull(final Iterator iter) {
        if (iter == null) {
            return ObjIterator.empty();
        }

        return new ObjIterator() {
            private final Iterator iterator = iter;
            private T next;

            @Override
            public boolean hasNext() {
                if (next == null && iterator.hasNext()) {
                    next = iterator.next();

                    if (next == null) {
                        while (iterator.hasNext()) {
                            next = iterator.next();

                            if (next != null) {
                                break;
                            }
                        }
                    }
                }

                return next != null;
            }

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

                final T result = next;
                next = null;
                return result;
            }
        };
    }

    public static  ImmutableIterator generate(final BooleanSupplier hasNext, final Supplier next) {
        return new ImmutableIterator() {
            @Override
            public boolean hasNext() {
                return hasNext.getAsBoolean();
            }

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

    public static  ImmutableIterator generate(final U seed, final Predicate hasNext, final Function next) {
        return new ImmutableIterator() {
            @Override
            public boolean hasNext() {
                return hasNext.test(seed);
            }

            @Override
            public T next() {
                return next.apply(seed);
            }
        };
    }
}