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

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

Go to download

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

There is a newer version: 5.2.4
Show newest version
/*
 * Copyright (c) 2018, 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.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;

import com.landawn.abacus.util.Fn.Suppliers;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.function.IndexedConsumer;
import com.landawn.abacus.util.stream.Stream;

/**
 *
 * @author Haiyang Li
 * @param 
 * @param 
 * @since 1.2.10
 */
public abstract class BiIterator extends ImmutableIterator> {

    @SuppressWarnings("rawtypes")
    private static final BiIterator EMPTY = new BiIterator() {
        @Override
        public boolean hasNext() {
            return false;
        }

        @Override
        public Object next() {
            throw new NoSuchElementException();
        }

        @Override
        protected void next(Throwables.BiConsumer action) throws NoSuchElementException {
            throw new NoSuchElementException();
        }

        @Override
        public void forEachRemaining(Throwables.BiConsumer action) throws Exception {
            N.checkArgNotNull(action);
        }

        @Override
        public ObjIterator map(BiFunction mapper) {
            N.checkArgNotNull(mapper);

            return ObjIterator.empty();
        }
    };

    /**
     *
     * @param 
     * @param 
     * @return
     */
    public static  BiIterator empty() {
        return EMPTY;
    }

    /**
     *
     * @param  the key type
     * @param  the value type
     * @param map
     * @return
     */
    public static  BiIterator of(final Map map) {
        if (N.isNullOrEmpty(map)) {
            return empty();
        }

        return of(map.entrySet().iterator());
    }

    /**
     *
     * @param  the key type
     * @param  the value type
     * @param iter
     * @return
     */
    public static  BiIterator of(final Iterator> iter) {
        if (iter == null) {
            return empty();
        }

        return new BiIterator<>() {

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

            @Override
            public Pair next() {
                return Pair.from(iter.next());
            }

            @Override
            protected  void next(final Throwables.BiConsumer action) throws NoSuchElementException, E {
                // N.checkArgNotNull(action);

                final Map.Entry entry = iter.next();

                action.accept(entry.getKey(), entry.getValue());
            }

            @Override
            public  void forEachRemaining(final Throwables.BiConsumer action) throws E {
                N.checkArgNotNull(action);

                Map.Entry entry = null;

                while (iter.hasNext()) {
                    entry = iter.next();
                    action.accept(entry.getKey(), entry.getValue());
                }
            }

            @Override
            public  ObjIterator map(final BiFunction mapper) {
                N.checkArgNotNull(mapper);

                return new ObjIterator<>() {
                    private Map.Entry entry = null;

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

                    @Override
                    public R next() {
                        entry = iter.next();

                        return mapper.apply(entry.getKey(), entry.getValue());
                    }
                };
            }
        };
    }

    /**
     * Returns an infinite {@code BiIterator}.
     *
     * @param 
     * @param 
     * @param output transfer the next values.
     * @return
     */
    public static  BiIterator generate(final Consumer> output) {
        return generate(com.landawn.abacus.util.function.BooleanSupplier.TRUE, output);
    }

    /**
     *
     * @param 
     * @param 
     * @param hasNext
     * @param output
     * @return
     */
    public static  BiIterator generate(final BooleanSupplier hasNext, final Consumer> output) {
        N.checkArgNotNull(hasNext);
        N.checkArgNotNull(output);

        return new BiIterator<>() {
            private final Pair tmp = new Pair<>();

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

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

                output.accept(tmp);

                return Pair.of(tmp.left, tmp.right);
            }

            @Override
            protected  void next(final Throwables.BiConsumer action) throws NoSuchElementException, E {
                // N.checkArgNotNull(action);

                if (!hasNext()) {
                    throw new NoSuchElementException();
                }

                output.accept(tmp);

                action.accept(tmp.left, tmp.right);
            }

            @Override
            public  void forEachRemaining(final Throwables.BiConsumer action) throws E {
                N.checkArgNotNull(action);

                while (hasNext.getAsBoolean()) {
                    output.accept(tmp);

                    action.accept(tmp.left, tmp.right);
                }
            }

            @Override
            public  ObjIterator map(final BiFunction mapper) {
                N.checkArgNotNull(mapper);

                return new ObjIterator<>() {
                    @Override
                    public boolean hasNext() {
                        return hasNext.getAsBoolean();
                    }

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

                        output.accept(tmp);

                        return mapper.apply(tmp.left, tmp.right);
                    }
                };
            }
        };
    }

    /**
     *
     * @param 
     * @param 
     * @param fromIndex
     * @param toIndex
     * @param output
     * @return
     */
    public static  BiIterator generate(final int fromIndex, final int toIndex, final IndexedConsumer> output) {
        N.checkFromToIndex(fromIndex, toIndex, Integer.MAX_VALUE);
        N.checkArgNotNull(output);

        return new BiIterator<>() {
            private final MutableInt cursor = MutableInt.of(fromIndex);
            private final Pair tmp = new Pair<>();

            @Override
            public boolean hasNext() {
                return cursor.value() < toIndex;
            }

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

                output.accept(cursor.getAndIncrement(), tmp);

                return Pair.of(tmp.left, tmp.right);
            }

            @Override
            protected  void next(final Throwables.BiConsumer action) throws NoSuchElementException, E {
                // N.checkArgNotNull(action);

                if (!hasNext()) {
                    throw new NoSuchElementException();
                }

                output.accept(cursor.getAndIncrement(), tmp);

                action.accept(tmp.left, tmp.right);
            }

            @Override
            public  void forEachRemaining(final Throwables.BiConsumer action) throws E {
                N.checkArgNotNull(action);

                while (cursor.value() < toIndex) {
                    output.accept(cursor.getAndIncrement(), tmp);

                    action.accept(tmp.left, tmp.right);
                }
            }

            @Override
            public  ObjIterator map(final BiFunction mapper) {
                N.checkArgNotNull(mapper);

                return new ObjIterator<>() {
                    @Override
                    public boolean hasNext() {
                        return cursor.value() < toIndex;
                    }

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

                        output.accept(cursor.getAndIncrement(), tmp);

                        return mapper.apply(tmp.left, tmp.right);
                    }
                };
            }
        };
    }

    /**
     *
     * @param 
     * @param 
     * @param a
     * @param b
     * @return
     */
    public static  BiIterator zip(final A[] a, final B[] b) {
        return zip(Array.asList(a), Array.asList(b));
    }

    /**
     *
     * @param 
     * @param 
     * @param a
     * @param b
     * @param valueForNoneA
     * @param valueForNoneB
     * @return
     */
    public static  BiIterator zip(final A[] a, final B[] b, final A valueForNoneA, final B valueForNoneB) {
        return zip(Array.asList(a), Array.asList(b), valueForNoneA, valueForNoneB);
    }

    /**
     *
     * @param 
     * @param 
     * @param a
     * @param b
     * @return
     */
    public static  BiIterator zip(final Iterable a, final Iterable b) {
        return zip(a == null ? null : a.iterator(), b == null ? null : b.iterator());
    }

    /**
     *
     * @param 
     * @param 
     * @param a
     * @param b
     * @param valueForNoneA
     * @param valueForNoneB
     * @return
     */
    public static  BiIterator zip(final Iterable a, final Iterable b, final A valueForNoneA, final B valueForNoneB) {
        return zip(a == null ? null : a.iterator(), b == null ? null : b.iterator(), valueForNoneA, valueForNoneB);
    }

    /**
     *
     * @param 
     * @param 
     * @param iterA
     * @param iterB
     * @return
     */
    public static  BiIterator zip(final Iterator iterA, final Iterator iterB) {
        if (iterA == null || iterB == null) {
            return empty();
        }

        return new BiIterator<>() {
            @Override
            public boolean hasNext() {
                return iterA.hasNext() && iterB.hasNext();
            }

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

                return Pair.of(iterA.next(), iterB.next());
            }

            @Override
            protected  void next(final Throwables.BiConsumer action) throws NoSuchElementException, E {
                // N.checkArgNotNull(action);

                action.accept(iterA.next(), iterB.next());
            }

            @Override
            public  void forEachRemaining(final Throwables.BiConsumer action) throws E {
                N.checkArgNotNull(action);

                while (iterA.hasNext() && iterB.hasNext()) {
                    action.accept(iterA.next(), iterB.next());
                }
            }

            @Override
            public  ObjIterator map(final BiFunction mapper) {
                N.checkArgNotNull(mapper);

                return new ObjIterator<>() {
                    @Override
                    public boolean hasNext() {
                        return iterA.hasNext() && iterB.hasNext();
                    }

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

                        return mapper.apply(iterA.next(), iterB.next());
                    }
                };
            }
        };
    }

    /**
     *
     * @param 
     * @param 
     * @param iterA
     * @param iterB
     * @param valueForNoneA
     * @param valueForNoneB
     * @return
     */
    public static  BiIterator zip(final Iterator iterA, final Iterator iterB, final A valueForNoneA, final B valueForNoneB) {
        final Iterator iter1 = iterA == null ? ObjIterator. empty() : iterA;
        final Iterator iter2 = iterB == null ? ObjIterator. empty() : iterB;

        return new BiIterator<>() {
            @Override
            public boolean hasNext() {
                return iter1.hasNext() || iter2.hasNext();
            }

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

                return Pair.of(iter1.hasNext() ? iter1.next() : valueForNoneA, iter2.hasNext() ? iter2.next() : valueForNoneB);
            }

            @Override
            protected  void next(final Throwables.BiConsumer action) throws NoSuchElementException, E {
                // N.checkArgNotNull(action);

                if (!hasNext()) {
                    throw new NoSuchElementException();
                }

                action.accept(iter1.hasNext() ? iter1.next() : valueForNoneA, iter2.hasNext() ? iter2.next() : valueForNoneB);
            }

            @Override
            public  void forEachRemaining(final Throwables.BiConsumer action) throws E {
                N.checkArgNotNull(action);

                while (iter1.hasNext() || iter2.hasNext()) {
                    action.accept(iter1.hasNext() ? iter1.next() : valueForNoneA, iter2.hasNext() ? iter2.next() : valueForNoneB);
                }
            }

            @Override
            public  ObjIterator map(final BiFunction mapper) {
                N.checkArgNotNull(mapper);

                return new ObjIterator<>() {
                    @Override
                    public boolean hasNext() {
                        return iter1.hasNext() || iter2.hasNext();
                    }

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

                        return mapper.apply(iter1.hasNext() ? iter1.next() : valueForNoneA, iter2.hasNext() ? iter2.next() : valueForNoneB);
                    }
                };
            }
        };
    }

    /**
     *
     * @param 
     * @param 
     * @param 
     * @param iter
     * @param unzipFunc output parameter.
     * @return
     */
    public static  BiIterator unzip(final Iterable iter, final BiConsumer> unzipFunc) {
        if (iter == null) {
            return BiIterator.empty();
        }

        return unzip(iter.iterator(), unzipFunc);
    }

    /**
     *
     * @param 
     * @param 
     * @param 
     * @param iter
     * @param unzipFunc output parameter.
     * @return
     */
    public static  BiIterator unzip(final Iterator iter, final BiConsumer> unzipFunc) {
        if (iter == null) {
            return BiIterator.empty();
        }

        final BooleanSupplier hasNext = iter::hasNext;

        final Consumer> output = out -> unzipFunc.accept(iter.next(), out);

        return BiIterator.generate(hasNext, output);
    }

    /**
     *
     * @param 
     * @param action
     * @throws NoSuchElementException
     * @throws E
     */
    protected abstract  void next(final Throwables.BiConsumer action) throws NoSuchElementException, E;

    /**
     * For each remaining.
     *
     * @param 
     * @param action
     * @throws E the e
     */
    public abstract  void forEachRemaining(final Throwables.BiConsumer action) throws E;

    /**
     * It's preferred to call forEachRemaining(Try.BiConsumer) to avoid the create the unnecessary Pair Objects.
     *
     * @param action
     * @deprecated
     */
    @Override
    @Deprecated
    public void forEachRemaining(java.util.function.Consumer> action) {
        super.forEachRemaining(action);
    }

    @SuppressWarnings("rawtypes")
    private static final Throwables.BiConsumer DO_NOTHING = (a, b) -> {
        // do nothing;
    };

    /**
     * 
     *
     * @param n 
     * @return 
     */
    public BiIterator skip(final long n) {
        N.checkArgNotNegative(n, "n");

        if (n <= 0) {
            return this;
        }

        final BiIterator iter = this;

        return new BiIterator<>() {
            private boolean skipped = false;

            @Override
            public boolean hasNext() {
                if (!skipped) {
                    skip();
                }

                return iter.hasNext();
            }

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

                return iter.next();
            }

            @Override
            protected  void next(final Throwables.BiConsumer action) throws NoSuchElementException, E {
                if (!skipped) {
                    skip();
                }

                iter.next(action);
            }

            @Override
            public  void forEachRemaining(final Throwables.BiConsumer action) throws E {
                if (!skipped) {
                    skip();
                }

                iter.forEachRemaining(action);
            }

            @Override
            public  ObjIterator map(final BiFunction mapper) {
                if (!skipped) {
                    skip();
                }

                return iter.map(mapper);
            }

            private void skip() {
                long idx = 0;

                final Throwables.BiConsumer action = DO_NOTHING;

                while (idx++ < n && iter.hasNext()) {
                    iter.next(action);
                }

                skipped = true;
            }
        };
    }

    /**
     * 
     *
     * @param count 
     * @return 
     */
    public BiIterator limit(final long count) {
        N.checkArgNotNegative(count, "count");

        if (count == 0) {
            return BiIterator. empty();
        }

        final BiIterator iter = this;

        return new BiIterator<>() {
            private long cnt = count;

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

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

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

            @Override
            protected  void next(final Throwables.BiConsumer action) throws NoSuchElementException, E {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }

                cnt--;
                iter.next(action);
            }

            @Override
            public  void forEachRemaining(final Throwables.BiConsumer action) throws E {
                while (hasNext()) {
                    cnt--;
                    iter.next(action);
                }
            }

            @Override
            public  ObjIterator map(final BiFunction mapper) {
                if (cnt > 0) {
                    return iter. map(mapper).limit(cnt);
                } else {
                    return ObjIterator. empty();
                }
            }
        };
    }

    /**
     * 
     *
     * @param predicate 
     * @return 
     */
    public BiIterator filter(final BiPredicate predicate) {
        N.checkArgNotNull(predicate, "predicate");

        final BiIterator iter = this;

        return new BiIterator<>() {
            private final Pair next = new Pair<>();
            private final Throwables.BiConsumer setNext = (a, b) -> next.set(a, b);

            private boolean hasNext = false;

            @Override
            public boolean hasNext() {
                if (!hasNext) {
                    while (iter.hasNext()) {
                        iter.next(setNext);

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

                return hasNext;
            }

            @Override
            public Pair next() {
                if (!hasNext && !hasNext()) {
                    throw new NoSuchElementException();
                }

                hasNext = false;

                return next.copy();
            }

            @Override
            protected  void next(final Throwables.BiConsumer action) throws NoSuchElementException, E {
                if (!hasNext && !hasNext()) {
                    throw new NoSuchElementException();
                }

                hasNext = false;

                action.accept(next.left, next.right);
            }

            @Override
            public  void forEachRemaining(final Throwables.BiConsumer action) throws E {
                while (hasNext()) {
                    hasNext = false;

                    action.accept(next.left, next.right);
                }
            }

            @Override
            public  ObjIterator map(final BiFunction mapper) {
                return new ObjIterator<>() {
                    @Override
                    public boolean hasNext() {
                        if (!hasNext) {
                            while (iter.hasNext()) {
                                iter.next(setNext);

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

                        return hasNext;
                    }

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

                        hasNext = false;

                        return mapper.apply(next.left, next.right);
                    }
                };
            }
        };
    }

    /**
     *
     * @param 
     * @param mapper
     * @return
     */
    public abstract  ObjIterator map(final BiFunction mapper);

    /**
     * 
     *
     * @return 
     */
    public Optional> first() {
        if (hasNext()) {
            return Optional.of(next());
        } else {
            return Optional.> empty();
        }
    }

    /**
     * 
     *
     * @return 
     */
    public Optional> last() {
        if (hasNext()) {
            final Pair next = new Pair<>();
            final Throwables.BiConsumer setNext = (a, b) -> next.set(a, b);

            forEachRemaining(setNext);

            return Optional.of(next);
        } else {
            return Optional.> empty();
        }
    }

    /**
     *
     * @param 
     * @param mapper
     * @return
     */
    public  Stream stream(final BiFunction mapper) {
        N.checkArgNotNull(mapper);

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

    /**
     * 
     *
     * @return 
     */
    public Pair[] toArray() {
        return toArray(new Pair[0]);
    }

    /**
     *
     * @param 
     * @param a
     * @return
     * @deprecated
     */
    @Deprecated
    public  T[] toArray(final T[] a) {
        return toList().toArray(a);
    }

    /**
     * 
     *
     * @return 
     */
    public List> toList() {
        return toCollection(Suppliers.> ofList());
    }
}