io.lacuna.bifurcan.utils.Iterators Maven / Gradle / Ivy
package io.lacuna.bifurcan.utils;
import io.lacuna.bifurcan.LinearList;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.LongFunction;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* @author ztellman
*/
public class Iterators {
private static final Object NONE = new Object();
public static final Iterator EMPTY = new Iterator() {
@Override
public boolean hasNext() {
return false;
}
@Override
public Object next() {
throw new NoSuchElementException();
}
};
/**
* A utility class for dynamically appending and prepending iterators to a collection, which itself can be iterated
* over.
*/
public static class IteratorStack implements Iterator {
LinearList> iterators = new LinearList<>();
public IteratorStack() {
}
public IteratorStack(Iterator... its) {
for (Iterator it : its) {
iterators.addFirst(it);
}
}
private void primeIterator() {
while (iterators.size() > 0 && !iterators.first().hasNext()) {
iterators.removeFirst();
}
}
@Override
public boolean hasNext() {
primeIterator();
return iterators.size() > 0 && iterators.first().hasNext();
}
@Override
public V next() {
primeIterator();
if (iterators.size() == 0) {
throw new NoSuchElementException();
}
return iterators.first().next();
}
public void addFirst(Iterator it) {
iterators.addFirst(it);
}
public void addLast(Iterator it) {
iterators.addLast(it);
}
}
public static boolean equals(Iterator a, Iterator b, BiPredicate equals) {
while (a.hasNext()) {
if (!equals.test(a.next(), b.next())) {
return false;
}
}
return true;
}
public static Iterator singleton(V val) {
return new Iterator() {
boolean consumed = false;
@Override
public boolean hasNext() {
return !consumed;
}
@Override
public V next() {
if (!consumed) {
consumed = true;
return val;
} else {
throw new NoSuchElementException();
}
}
};
}
/**
* @param it an iterator
* @param f a predicate
* @return an iterator which only yields values that satisfy the predicate
*/
public static Iterator filter(Iterator it, Predicate f) {
return new Iterator() {
private Object next = NONE;
private boolean done = false;
private void prime() {
if (next == NONE && !done) {
while (it.hasNext()) {
next = it.next();
if (f.test((V) next)) {
return;
}
}
done = true;
}
}
@Override
public boolean hasNext() {
prime();
return !done;
}
@Override
public V next() {
prime();
if (next == NONE) {
throw new NoSuchElementException();
}
V val = (V) next;
next = NONE;
return val;
}
};
}
/**
* @param it an iterator
* @param f a function which transforms values
* @return an iterator which yields the transformed values
*/
public static Iterator map(Iterator it, Function f) {
return new Iterator() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public V next() {
return f.apply(it.next());
}
};
}
/**
* @param it an iterator
* @param f a function which transforms values into iterators
* @return an iterator which yields the concatenation of the iterators
*/
public static Iterator flatMap(Iterator it, Function> f) {
return new Iterator() {
Iterator curr = EMPTY;
private void prime() {
while (!curr.hasNext() && it.hasNext()) {
curr = f.apply(it.next());
}
}
@Override
public boolean hasNext() {
prime();
return curr.hasNext();
}
@Override
public V next() {
prime();
return curr.next();
}
};
}
/**
* @param min an inclusive start of the range
* @param max an exclusive end of the range
* @param f a function which transforms a number in the range into a value
* @return an iterator which yields the values returned by {@code f}
*/
public static Iterator range(long min, long max, LongFunction f) {
return new Iterator() {
long i = min;
@Override
public boolean hasNext() {
return i < max;
}
@Override
public V next() {
if (hasNext()) {
return f.apply(i++);
} else {
throw new NoSuchElementException();
}
}
};
}
/**
* Represents a range implicitly starting at 0.
*
* @param max an exclusive end of the range.
* @param f a function which transforms a number in the range into a value.
* @return an iterator which yields the values returned by {@code f}
*/
public static Iterator range(long max, LongFunction f) {
return range(0, max, f);
}
/**
* @param iterators a list of iterators
* @return a concatenation of all iterators, in the order provided
*/
public static Iterator concat(Iterator... iterators) {
if (iterators.length == 1) {
return iterators[0];
} else {
IteratorStack stack = new IteratorStack();
for (Iterator it : iterators) {
stack.addLast(it);
}
return stack;
}
}
public static Stream toStream(Iterator it) {
return toStream(it, 0);
}
public static Stream toStream(Iterator it, long estimatedSize) {
return StreamSupport.stream(Spliterators.spliterator(it, estimatedSize, Spliterator.ORDERED), false);
}
}