nl.vpro.util.MergedSortedIterator Maven / Gradle / Ivy
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 super T> 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 super T> comparator, CountedIterator... iterators) {
return mergeInSameThread(comparator, Arrays.asList(iterators));
}
@SuppressWarnings("UnstableApiUsage")
public static MergedSortedIterator merge(Comparator super T> 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 super T> 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 super S> comparator;
Optional next = null;
boolean needsFindNext = true;
SameThreadMergingIterator(Comparator super S> comparator, Iterable extends Iterator> 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;
}
}
}
}