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

nl.vpro.util.MergedSortedIterator Maven / Gradle / Ivy

There is a newer version: 5.3.1
Show newest version
package nl.vpro.util;

import java.util.*;
import java.util.function.Supplier;

import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;

/**
 * @author Michiel Meeuwissen
 * @since 0.32
 */
public class MergedSortedIterator  extends BasicWrappedIterator implements  CountedIterator {

    protected MergedSortedIterator(Supplier size, Supplier totalSize, Iterator iterator) {
        super(size, totalSize, null, null, iterator);
    }

    /**
     * This uses guava's {@link Iterators#mergeSorted}. This probable performs well, but will use a queue.
     *
     */
    @SafeVarargs
    public static  MergedSortedIterator merge(Comparator comparator, CountedIterator... iterators) {
        return merge(comparator, Arrays.asList(iterators));
    }

    /**
     * This doesn't use a queue, so it is also useable with Hibernate.
     */
    @SafeVarargs
    public static  MergedSortedIterator mergeInSameThread(Comparator comparator, CountedIterator... iterators) {
        return mergeInSameThread(comparator, Arrays.asList(iterators));
    }

    @SuppressWarnings("UnstableApiUsage")
    public static  MergedSortedIterator merge(Comparator comparator, Iterable> iterators) {
        return new MergedSortedIterator<>(
            () -> getSize(iterators),
            () -> getTotalSize(iterators),
            Iterators.mergeSorted(iterators, comparator));
    }

    /**
     * This doesn't usea  queue, so it is also useable with Hibernate.
     */
    public static  MergedSortedIterator mergeInSameThread(Comparator comparator, Iterable> iterators) {
        return new MergedSortedIterator<>(() -> getSize(iterators), () -> getTotalSize(iterators), new SameThreadMergingIterator<>(comparator, iterators));
    }

    protected static  Long getSize(Iterable> iterators) {
        Long size = 0L;
        for (CountedIterator c : iterators) {
            Optional s = c.getSize();
            if (s.isPresent()) {
                size += s.get();
            } else {
                size = null;
                break;
            }
        }
        return size;
    }

    protected static  Long getTotalSize(Iterable> iterators) {
        Long size = 0L;
        for (CountedIterator c : iterators) {
            Optional s = c.getTotalSize();
            if (s.isPresent()) {
                size += s.get();
            } else {
                size = null;
                break;
            }
        }
        return size;
    }

    @SuppressWarnings({"OptionalAssignedToNull", "OptionalUsedAsFieldOrParameterType"})
    protected static class SameThreadMergingIterator implements Iterator {
        final List> iterators;
        final Comparator comparator;
        Optional next = null;
        boolean needsFindNext = true;


        SameThreadMergingIterator(Comparator comparator, Iterable> iterators) {
            this.comparator = comparator;
            this.iterators = new ArrayList<>();
            for (Iterator i : iterators) {
                this.iterators.add(Iterators.peekingIterator(i));
            }
        }

        @Override
        public boolean hasNext() {
            findNext();
            return next != null;
        }

        @Override
        public S next() {
            findNext();
            if (next == null){
                throw new NoSuchElementException();
            }
            needsFindNext = true;
            return next.orElse(null);
        }
        protected void findNext() {
            if (needsFindNext) {
                Optional candidate = null;
                PeekingIterator usedIterator = null;
                for (PeekingIterator i : iterators) {
                    if (i.hasNext()) {
                        if (candidate == null || comparator.compare(candidate.orElse(null), i.peek()) >= 0) {
                            candidate = Optional.ofNullable(i.peek());
                            usedIterator = i;
                        }
                    }
                }
                next = candidate;
                if (usedIterator != null) {
                    usedIterator.next();
                }
                needsFindNext = false;
            }
        }
    }

}