org.apache.jena.atlas.iterator.Iter Maven / Gradle / Ivy
Show all versions of jena-base Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.jena.atlas.iterator;
import java.io.PrintStream;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.apache.jena.atlas.io.IO;
import org.apache.jena.atlas.lib.Closeable;
import org.apache.jena.atlas.lib.Sink;
/**
* Iter provides general utilities for working with {@linkplain Iterator Iterators}.
* This class provides functionality similar to {@code Stream}
* except for iterators (and hence single threaded).
*
* Style 1: functional style using statics.
*
*
* import static org.apache.jena.atlas.iterator.Iter.*;
*
* filter(map(iterator, function), predicate)
*
*
* Style 2: Stream-like: The class {@code Iter} provides methods to call on an iterator.
*
*
* import static org.apache.jena.atlas.iterator.Iter.iter;
*
* iter(iterator).map(...).filter(...)
*
*
* The operations attempt to close iterators - see {@link Iter#close(Iterator)}.
* Caution - not all Iterators in this package provide close or passdown close operations
* {@link IteratorOnClose} adds a {@link Runnable} action called once on close.
*
* Iterators do not guarantee {@code .remove}.
*
* @param the type of element over which an instance of {@code Iter} iterates,
*/
public class Iter implements IteratorCloseable {
/** Shorter form of "forEachRemaining" */
public static void forEach(Iterator iter, Consumer action) {
iter.forEachRemaining(action);
}
public static Stream asStream(Iterator iterator) {
return asStream(iterator, false);
}
public static Stream asStream(Iterator iterator, boolean parallel) {
int characteristics = Spliterator.IMMUTABLE;
Stream stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, characteristics), parallel);
stream.onClose(()->close(iterator));
return stream;
}
// ---- Special iterators.
public static Iterator singleton(T item) {
// There is a singleton iterator in Collections but it is not public.
return new SingletonIterator<>(item);
}
public static Iterator nullIterator() {
return Collections.emptyIterator();
}
// -- Stream inspired names
public static Iter empty() {
return Iter.iter(Collections.emptyIterator());
}
public static Iter of(T item) {
return Iter.iter(new SingletonIterator<>(item));
}
@SafeVarargs
public static Iter of(T ...items) {
return Iter.iter(List.of(items).iterator());
}
public static Iter ofNullable(T t) {
return t == null ? Iter.empty() : Iter.of(t);
}
/**
* Return an iterator that does not permit remove.
* This makes an "UnmodifiableIterator".
*/
public static Iterator noRemove(Iterator iter) {
return new IteratorWrapper(iter) {
@Override public final void remove() {
throw new UnsupportedOperationException("remove");
}
};
}
// ---- Collectors.
/** Collect an iterator into a set. */
public static Set toSet(Iterator extends T> stream) {
return collect(stream, Collectors.toSet());
}
/** Collect an iterator into a list. */
public static List toList(Iterator extends T> stream) {
return collect(stream, Collectors.toList());
}
/**
* Create another iterator without risk of concurrent modification
* exceptions. This materializes the input iterator.
*/
public static Iterator iterator(Iterator extends T> iterator) {
List x = Iter.toList(iterator);
return x.iterator();
}
// Note fold-left and fold-right
// http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29
// This reduce is a kind of fold-left (take first element, apply to rest of list)
// and can deal with lists of zero elements.
// -- Operations on iterators.
@FunctionalInterface
public interface Folder extends BiFunction {}
public static R foldLeft(Iterator extends T> stream, R value, Folder function) {
// Tail recursion, unwound
for (; stream.hasNext();) {
T item = stream.next();
value = function.apply(value, item);
}
return value;
}
public static R foldRight(Iterator extends T> stream, R value, Folder function) {
// Recursive.
if ( !stream.hasNext() )
return value;
T item = stream.next();
return function.apply(foldRight(stream, value, function), item);
}
public static Optional reduce(Iterator iter, BinaryOperator accumulator) {
T r = reduce(iter, null, accumulator);
return Optional.ofNullable(r);
}
public static T reduce(Iterator iter, T identity, BinaryOperator accumulator) {
T result = identity;
while(iter.hasNext()) {
T elt = iter.next();
result = (result == null) ? elt : accumulator.apply(result, elt);
}
return result;
}
// ---- min and max
public static Optional min(Iterator iter, Comparator comparator) {
T x = null;
while(iter.hasNext()) {
T elt = iter.next();
if ( x == null )
x = elt;
else {
int cmp = comparator.compare(x, elt);
if ( cmp > 0 )
x = elt;
}
}
return Optional.ofNullable(x);
}
public static Optional max(Iterator iter, Comparator comparator) {
// Or min(iter, comparator.reversed())
T x = null;
while(iter.hasNext()) {
T elt = iter.next();
if ( x == null )
x = elt;
else {
int cmp = comparator.compare(x, elt);
if ( cmp < 0 )
x = elt;
}
}
return Optional.ofNullable(x);
}
// ---- collect
/** See {@link Stream#collect(Supplier, BiConsumer, BiConsumer)}, except without the {@code BiConsumer combiner} */
public static R collect(Iterator iter, Supplier supplier, BiConsumer accumulator) {
R result = supplier.get();
iter.forEachRemaining(elt -> accumulator.accept(result, elt));
return result;
}
/** See {@link Stream#collect(Collector)} */
public static R collect(Iterator iter, Collector super T, A, R> collector) {
A a = collect(iter, collector.supplier(), collector.accumulator());
return collector.finisher().apply(a);
}
/** Act on elements of an iterator.
* @see #map(Iterator, Function)
*/
public static void apply(Iterator extends T> stream, Consumer action) {
stream.forEachRemaining(action);
}
// ---- Filter
public static Iterator filter(final Iterator extends T> stream, final Predicate filter) {
return new IterFiltered(stream, filter);
}
public static Iterator notFilter(final Iterator extends T> stream, final Predicate filter) {
return filter(stream, filter.negate());
}
// Filter-related
private static final class IterFiltered implements IteratorCloseable {
private final Iterator extends T> stream;
private final Predicate filter;
private boolean finished = false;
private boolean slotOccupied = false;
private T slot;
private IterFiltered(Iterator extends T> stream, Predicate filter) {
this.stream = stream;
this.filter = filter;
}
@Override
public boolean hasNext() {
if ( finished )
return false;
while (!slotOccupied) {
if ( !stream.hasNext() ) {
closeIterator();
break;
}
T nextItem = stream.next();
if ( filter.test(nextItem) ) {
slot = nextItem;
slotOccupied = true;
break;
}
}
return slotOccupied;
}
@Override
public T next() {
if ( hasNext() ) {
slotOccupied = false;
return slot;
}
closeIterator();
throw new NoSuchElementException("filter.next");
}
@Override
public void forEachRemaining(Consumer super T> action) {
if ( finished )
return;
if ( slotOccupied ) {
action.accept(slot);
}
T t;
while (stream.hasNext()) {
t = stream.next();
if ( filter.test(t) )
action.accept(t);
}
slotOccupied = false;
}
private void closeIterator() {
if ( finished )
return;
finished = true;
Iter.close(stream);
}
@Override
public void close() {
closeIterator();
}
}
/**
* Return true if every element of stream passes the filter (reads the
* stream until the first element not passing the filter)
*/
public static boolean allMatch(Iterator iter, Predicate super T> predicate) {
try {
while (iter.hasNext()) {
T item = iter.next();
if ( !predicate.test(item) ) {
Iter.close(iter);
return false;
}
}
return true;
}
finally { Iter.close(iter); }
}
/**
* Return true if one or more elements of stream passes the filter (reads the
* stream to first element passing the filter)
*/
public static boolean anyMatch(Iterator iter, Predicate super T> predicate) {
try {
while (iter.hasNext()) {
T item = iter.next();
if ( predicate.test(item) ) {
Iter.close(iter);
return true;
}
}
return false;
}
finally { Iter.close(iter); }
}
/**
* Return true if none of the elements of the iterator passes the predicate test reads
* the stream to first element passing the filter)
*/
public static boolean noneMatch(Iterator iter, Predicate super T> predicate) {
return ! anyMatch(iter, predicate);
}
/**
* Return an Optional with the first element of an iterator that matches the predicate.
* Return {@code Optional.empty} if none match.
* Reads the iterator until the first match.
*/
public static Optional findFirst(Iterator iter, Predicate super T> predicate) {
while ( iter.hasNext() ) {
T item = iter.next();
if ( predicate.test(item) )
return Optional.of(item);
}
return Optional.empty();
}
/**
* Return an Optional with the last element of an iterator that matches the predicate.
* Return {@code Optional.empty} if no match.
* Reads the iterator to completion.
*/
public static Optional findLast(Iterator iter, Predicate super T> predicate) {
T thing = null;
while ( iter.hasNext() ) {
T item = iter.next();
if ( predicate.test(item) )
thing = item;
}
return Optional.ofNullable(thing);
}
/**
* Return an Optional with an element of an iterator that matches the predicate.
* Return {@code Optional.empty} if none match.
* The element returned is not specified by the API contract.
*/
public static Optional findAny(Iterator iter, Predicate super T> predicate) {
return findFirst(iter, predicate);
}
// ---- Map
/**
* Apply a function to every element of an iterator, transforming it
* from a {@code T} to an {@code R}.
*/
public static Iterator map(Iterator extends T> stream, Function converter) {
return new IterMap<>(stream, converter);
}
private static final class IterMap implements IteratorCloseable {
private final Iterator extends T> stream;
private final Function converter;
private IterMap(Iterator extends T> stream, Function converter) {
this.stream = stream;
this.converter = converter;
}
@Override
public boolean hasNext() {
return stream.hasNext();
}
@Override
public R next() {
return converter.apply(stream.next());
}
@Override
public void forEachRemaining(Consumer super R> action) {
stream.forEachRemaining(item->action.accept(converter.apply(item)));
}
@Override
public void close() {
Iter.close(stream);
}
}
/**
* Apply a function to every element of an iterator, to produce possibly multiple mapping each time.
* See {@link Stream#flatMap}
*/
public static Iterator flatMap(Iterator iter, Function> mapper) {
return new IteratorFlatMap<>(iter, mapper);
}
/**
* Apply an action to everything in stream, yielding a stream of the
* same items.
*/
public static Iterator operate(final Iterator extends T> stream, final Consumer action) {
final Iterator iter = new IterOperate(stream, action);
return iter;
}
private static final class IterOperate implements IteratorCloseable {
private final Iterator extends T> stream;
private final Consumer action;
private IterOperate(Iterator extends T> stream, Consumer action) {
this.stream = stream;
this.action = action;
}
@Override
public boolean hasNext() {
return stream.hasNext();
}
@Override
public T next() {
T t = stream.next();
action.accept(t);
return t;
}
@Override
public void forEachRemaining(Consumer super T> action) {
stream.forEachRemaining(item->{
this.action.accept(item);
action.accept(item);
});
}
@Override
public void close() {
Iter.close(stream);
}
}
/** Print an iterator as it gets used - this adds a printing wrapper */
public static Iterator printWrapper(final Iterator extends T> stream) {
return Iter.printWrapper(System.out, stream);
}
/** Print an iterator as it gets used - this adds a printing wrapper */
public static Iterator printWrapper(final PrintStream out, final Iterator extends T> stream) {
return Iter.operate(stream, out::println);
}
/** Join two iterators.
* If there, potentially, going to be many iterators, it is better to
* create an {@link IteratorConcat} explicitly and add each iterator.
*/
public static Iterator append(Iterator extends T> iter1, Iterator extends T> iter2) {
return IteratorCons.create(iter1, iter2);
}
/** Return an iterator that will see each element of the underlying iterator only once.
* Note that this need working memory to remember the elements already seen.
*/
public static Iterator distinct(Iterator iter) {
return filter(iter, new FilterUnique());
}
/** Remove adjacent duplicates. This operation does not need
* working memory to remember the all elements already seen,
* just a slot for the last element seen.
*/
public static Iterator distinctAdjacent(Iterator iter) {
return filter(iter, new FilterDistinctAdjacent());
}
/** Remove nulls from an iterator */
public static Iterator removeNulls(Iterator iter) {
return filter(iter, Objects::nonNull);
}
/** Step forward up to {@code steps} places.
*
Return number of steps taken.
*
* @apiNote
* The iterator is moved at most {@code steps} places with no overshoot.
* The iterator can be used afterwards.
*/
public static int step(Iterator> iter, int steps) {
for ( int i = 0; i < steps; i++) {
if ( ! iter.hasNext() )
return i;
iter.next();
}
return steps;
}
/** Take the first N elements of an iterator - stop early if too few
* @see #limit(Iterator, long)
*/
public static List take(Iterator iter, int N) {
iter = new IterLimit<>(iter, N);
List x = new ArrayList<>(N);
while ( iter.hasNext() )
x.add(iter.next());
return x;
}
/** Create an iterator such that it yields elements while a predicate test on
* the elements is true, end the iteration.
* @see Iter#filter(Iterator, Predicate)
*/
public static Iterator takeWhile(Iterator iter, Predicate predicate) {
return new IteratorTruncate<>(iter, predicate);
}
/**
* Create an iterator such that it yields elements until a predicate test on
* the elements becomes true, end the iteration.
*
* @see Iter#filter(Iterator, Predicate)
*/
public static Iterator takeUntil(Iterator iter, Predicate predicate) {
return new IteratorTruncate<>(iter, predicate.negate());
}
/** Create an iterator such that elements from the front while
* a predicate test become true are dropped then return all remaining elements
* are iterated over.
* The first element where the predicted becomes true is the first element of the
* returned iterator.
*/
public static Iterator dropWhile(Iterator iter, Predicate predicate) {
PeekIterator iter2 = new PeekIterator<>(iter);
for(;;) {
T elt = iter2.peek();
if ( elt == null )
return Iter.nullIterator();
if ( ! predicate.test(elt) )
break;
}
return iter2;
}
/** Create an iterator such that elements from the front until
* a predicate test become true are dropped then return all remaining elements
* are iterated over.
* The first element where the predicate becomes true is the first element of the
* returned iterator.
*/
public static Iterator dropUntil(Iterator iter, Predicate predicate) {
return dropWhile(iter, predicate.negate());
}
/** Return an iterator that is limited to the given number of elements.
* If it is shorter than the limit, stop at the end.
* @see #take(Iterator, int)
*/
public static Iterator limit(Iterator iterator, long limit) {
return new IterLimit<>(iterator, limit);
}
private static class IterLimit implements IteratorCloseable {
private long count = 0;
private final long limit;
private boolean closed = false;
private final Iterator extends T> iterator;
private IterLimit(Iterator extends T> stream, long limit) {
this.iterator = stream;
this.limit = limit;
}
@Override
public boolean hasNext() {
if ( count >= limit ) {
closeOnce();
return false;
}
boolean b = iterator.hasNext();
if ( !b )
closeOnce();
return b;
}
@Override
public T next() {
try {
T t = iterator.next();
count++;
return t;
} catch (NoSuchElementException ex) {
closeOnce();
throw ex;
}
}
private void closeOnce() {
if ( ! closed )
Iter.close(iterator);
closed = true;
}
@Override
public void close() { closeOnce(); }
@Override
public void remove() {
// But leave the count as-is.
iterator.remove();
}
}
/** Skip over a number of elements of an iterator */
public static Iterator skip(Iterator iterator, long limit) {
for ( long i = 0; i < limit; i++ ) {
if ( iterator.hasNext() )
iterator.next();
else
// Now exhausted.
break;
}
return iterator;
}
/** Count the iterator (this is destructive on the iterator) */
public static long count(Iterator iterator) {
ActionCount action = new ActionCount<>();
iterator.forEachRemaining(action);
return action.getCount();
}
/** Consume the iterator */
public static void consume(Iterator iterator) {
iterator.forEachRemaining(x->{}); // Do nothing.
}
/** Create a string from an iterator, using the separator. Note: this consumes the iterator. */
public static String asString(Iterator stream, String sep) {
return Iter.iter(stream).map(x->x.toString()).collect(Collectors.joining(sep));
}
/** Create a string from an iterator, using the separator, prefix and suffix. Note: this consumes the iterator. */
public static String asString(Iterator stream, CharSequence sep, CharSequence prefix, CharSequence suffix) {
return Iter.iter(stream).map(x->x.toString()).collect(Collectors.joining(sep, prefix, suffix));
}
/** Close iterator if marked with {@link Closeable}. */
public static void close(Iterator iter) {
if ( iter instanceof Closeable cIter )
cIter.close();
}
/**
* Run an action when an iterator is closed. This assumes the iterator closed
* with {@link Iter#close}.
*
* Convenience function for closing a {@link java.io.Closeable} after use when
* passing an iterator out of scope.
*/
public static IteratorCloseable onCloseIO(Iterator iterator, java.io.Closeable ioThing) {
return onClose(iterator, ()->IO.close(ioThing));
}
/**
* Run an action when an iterator is closed.
* This assumes the iterator closed with {@link Iter#close}.
*/
public static IteratorOnClose onClose(Iterator iter, Runnable closeHandler) {
return IteratorOnClose.atEnd(iter, closeHandler);
}
/**
* Print an iterator to stdout, return a copy of the iterator. Printing
* occurs now. See {@link #debug} for an operation to print as the
* iterator is used.
*/
public static Iterator log(Iterator stream) {
return log(System.out, stream);
}
/**
* Print an iterator to stdout, return a copy of the iterator. Printing
* occurs now. See {@link #debug} for an operation to print as the
* iterator is used.
*/
public static Iterator log(final PrintStream out, Iterator stream) {
Iterator iter = debug(out, stream);
// And force it to run.
if ( ! iter.hasNext() )
out.println("");
return Iter.toList(iter).iterator();
}
/**
* Print an iterator to stdout, return a copy of the iterator. Printing
* occurs when the iterator is used. See {@link #log} for
* an operation to print now.
*/
public static Iterator debug(Iterator stream) {
return debug(System.out, stream);
}
/**
* Print an iterator, return a copy of the iterator. Printing
* occurs as the returned iterator is used.
*/
public static Iterator debug(final PrintStream out, Iterator stream) {
try {
return map(stream, item -> {out.println(item); return item;});
} finally { out.flush(); }
}
/** Print an iterator (destructive) */
public static void print(Iterator stream) {
print(System.out, stream);
}
/** Print an iterator (destructive) */
public static void print(PrintStream out, Iterator stream) {
stream.forEachRemaining(out::println);
}
/** Send the elements of the iterator to a sink - consumes the iterator */
public static void sendToSink(Iterator iter, Sink sink) {
iter.forEachRemaining(sink::send);
sink.close();
}
// ----
// Iter class part : factories
public static Iter iter(Iter iter) {
return iter;
}
public static Iter iter(Collection collection) {
return iter(collection.iterator());
}
public static Iter iter(Iterator iterator) {
Objects.requireNonNull(iterator);
if ( iterator instanceof Iter iter )
return iter;
return new Iter<>(iterator);
}
public static Iter singletonIter(T item) {
return iter(new SingletonIterator<>(item));
}
public static Iter nullIter() {
return iter(new NullIterator());
}
/**
* Materialize an iterator, that is, force it to run now - useful in
* debugging or when iteration may modify underlying datastructures.
*/
public static Iterator materialize(Iterator iter) {
return toList(iter).iterator();
}
/** An {@code Iter} of 2 {@code Iter}'s */
public static Iter concat(Iter iter1, Iter iter2) {
if ( iter1 == null )
return iter2;
if ( iter2 == null )
return iter1;
return iter1.append(iter2);
}
/** An {@code Iterator} of 2 {@code Iterator}'s.
* See also {@link IteratorConcat}.
*/
public static Iter concat(Iterator iter1, Iterator iter2) {
if ( iter1 == null )
return iter(iter2);
if ( iter2 == null )
return iter(iter1);
return iter(iter1).append(iter(iter2));
}
/** Return the first element of an iterator or null if no such element.
* @param iter
* @return An item or null.
*/
public static T first(Iterator iter) {
return first(iter, (x)-> true );
}
/** Skip to the first element meeting a condition and return that element. */
public static T first(Iterator iter, Predicate filter) {
while (iter.hasNext()) {
T t = iter.next();
if ( filter.test(t) )
return t;
}
return null;
}
/** Skip to the first element meeting a condition and return that element's index (zero-based). */
public static int firstIndex(Iterator iter, Predicate filter) {
for (int idx = 0; iter.hasNext(); idx++) {
T t = iter.next();
if ( filter.test(t) )
return idx;
}
return -1;
}
/** Return the last element or null, if no elements. This operation consumes the iterator. */
public static T last(Iterator iter) {
return last(iter, (x)->true);
}
/** Return the last element satisfying a predicate. This operation consumes the whole iterator. */
public static T last(Iterator iter, Predicate super T> filter) {
T thing = null;
while (iter.hasNext()) {
T t = iter.next();
if ( filter.test(t) )
thing = t;
}
return thing;
}
/** Find the last occurrence, defined by a predicate, in a list. */
public static int lastIndex(List list, Predicate super T> filter) {
for (int idx = list.size()-1; idx >= 0; idx--) {
T t = list.get(idx);
if ( filter.test(t) )
return idx;
}
return -1;
}
/** reverse iterator for a list */
public static Iterator reverseIterate(List list) {
ListIterator iter = list.listIterator(list.size());
return new Iterator<>() {
@Override public boolean hasNext() { return iter.hasPrevious(); }
@Override public T next() { return iter.previous(); }
};
}
/** reverse for each on a list. */
public static void reverseIterate(List list, Consumer super T> action) {
ListIterator iter = list.listIterator(list.size());
while(iter.hasPrevious()) {
T t = iter.previous();
action.accept(t);
}
}
// ------------------------------------------------------
// The class.
private Iterator iterator;
private Iter(Iterator iterator) {
this.iterator = iterator;
}
@Override
public void close() {
Iter.close(iterator);
}
/** Apply the Consumer to each element of the iterator */
public void forEach(Consumer action) {
iterator.forEachRemaining(action);
}
@Override
public void forEachRemaining(Consumer super T> action) {
iterator.forEachRemaining(action);
}
/** Consume the {@code Iter} and produce a {@code Set} */
public Set toSet() {
return toSet(iterator);
}
/** Consume the {@code Iter} and produce a {@code List} */
public List toList() {
return toList(iterator);
}
public void sendToSink(Sink sink) {
sendToSink(iterator, sink);
}
public T first() {
return first(iterator);
}
public T last() {
return last(iterator);
}
/** Skip to the first element meeting a condition and return that element. */
public T first(Predicate