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

org.apache.jena.atlas.iterator.Iter Maven / Gradle / Ivy

/*
 * 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.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream ;
import java.util.stream.StreamSupport ;

import org.apache.jena.atlas.lib.Closeable ;
import org.apache.jena.atlas.lib.Sink ;

/**
 * Iter provides general utilities for working with {@link Iterator}s.
 * 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(...)}
 * 
* * @param the type of element over which an instance of Iter iterates, */ public class Iter implements Iterator { // IteratorSlotted needed? IteratorPeek // IteratorSlotted.inspect public static Stream asStream(Iterator iterator) { return asStream(iterator, false); } public static Stream asStream(Iterator iterator, boolean parallel) { // Why isn't there a JDK operation for Iterator -> (sequential) stream? int characteristics = 0 ; //Spliterator.IMMUTABLE; return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, characteristics), parallel); } // ---- Special iterators. public static Iterator singleton(T item) { // Theer is a single iterator in Co0llections but it is not public. return new SingletonIterator<>(item) ; } public static Iterator nullIterator() { // Java7 caught up. return Collections.emptyIterator(); } // ---- Collectors. /** Collect an iterator into a set. */ public static Set toSet(Iterator stream) { Set acc = new HashSet<>() ; collect(acc, stream) ; return acc ; } /** Collect an iterator into a list. */ public static List toList(Iterator stream) { List acc = new ArrayList<>() ; collect(acc, stream) ; return acc ; } /** Collect an iterator. */ private static void collect(Collection acc, Iterator stream) { stream.forEachRemaining((x)->acc.add(x)) ; } /** * Create another iterator without risk of concurrent modification * exceptions. This materializes the input iterator. */ public static Iterator iterator(Iterator iterator) { List x = Iter.toList(iterator) ; return x.iterator() ; } // -- Operations on iterators. public interface Folder { Y eval(Y acc, X arg) ; } public static R foldLeft(Iterable stream, Folder function, R value) { return foldLeft(stream.iterator(), function, value) ; } public static R foldLeft(Iterator stream, Folder function, R value) { // Tail recursion, unwound for (; stream.hasNext();) { T item = stream.next() ; value = function.eval(value, item) ; } return value ; } public static R foldRight(Iterable stream, Folder function, R value) { return foldRight(stream.iterator(), function, value) ; } public static R foldRight(Iterator stream, Folder function, R value) { // Recursive. if ( !stream.hasNext() ) return value ; T item = stream.next() ; return function.eval(foldRight(stream, function, value), item) ; } // Note fold-left and fold-right // http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29 // This reduce is fold-left (take first element, apply to rest of list) // which copes with infinite lists. // Fold-left starts by combining the first element, then moves on. /** Reduce by aggregator. * This reduce is fold-left (take first element, apply to rest of list) */ public static R reduce(Iterable stream, Accumulate aggregator) { return reduce(stream.iterator(), aggregator) ; } /** Reduce by aggregator. * This reduce is fold-left (take first element, apply to rest of list) */ public static R reduce(Iterator stream, Accumulate aggregator) { aggregator.start() ; for (; stream.hasNext();) { T item = stream.next() ; aggregator.accumulate(item) ; } aggregator.finish() ; return aggregator.get() ; } /** Act on elements of an iterator. * @see #map(Iterator, Function) */ public static void apply(Iterator stream, Consumer action) { for (; stream.hasNext();) { T item = stream.next() ; action.accept(item) ; } } // ---- Filter public static Iterator filter(final Iterator stream, final Predicate filter) { final Iterator iter = new Iterator() { boolean finished = false ; boolean slotOccupied = false ; T slot ; @Override public boolean hasNext() { if ( finished ) return false ; while (!slotOccupied) { if ( !stream.hasNext() ) { finished = true ; 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 ; } throw new NoSuchElementException("filter.next") ; } @Override public void remove() { throw new UnsupportedOperationException("filter.remove") ; } } ; return iter ; } public static Iterator notFilter(final Iterator stream, final Predicate filter) { return filter(stream, filter.negate()) ; } // Filter-related /** * Return true if every element of stream passes the filter (reads the * stream until the first element not passing the filter) */ public static boolean every(Iterator stream, Predicate filter) { while ( stream.hasNext() ) { T item = stream.next() ; if ( !filter.test(item) ) return false ; } return true ; } /** * Return true if one or more elements of stream passes the filter (reads * the stream to first element passing the filter) */ public static boolean some(Iterator stream, Predicate filter) { while ( stream.hasNext() ) { T item = stream.next() ; if ( filter.test(item) ) return true ; } return false ; } // ---- 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 stream, Function converter) { final Iterator iter = new Iterator() { @Override public boolean hasNext() { return stream.hasNext() ; } @Override public R next() { return converter.apply(stream.next()) ; } @Override public void remove() { throw new UnsupportedOperationException("map.remove") ; } } ; return iter ; } /** Transform a list of elements to a new list of the function applied to each element. * Using a stream is often better. This operation preseves the order of the list. * @deprecated Use Java8 Streams */ @Deprecated public static List map(List list, Function converter) { return toList(map(list.iterator(), converter)) ; } /** * Apply an action to everything in stream, yielding a stream of the * same items. */ public static Iterator operate(final Iterator stream, final Consumer action) { final Iterator iter = new Iterator() { @Override public boolean hasNext() { return stream.hasNext() ; } @Override public T next() { T t = stream.next() ; action.accept(t) ; return t ; } @Override public void remove() { throw new UnsupportedOperationException("operate.remove") ; } } ; return iter ; } /** Print an iterator as it gets used - this adds a printing wrapper */ public static Iterator printWrapper(final Iterator 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 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 iter1, Iterator 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 alreadey 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) ; } /** Take the first N elements of an iterator - stop early if too few */ public static List take(Iterator iter, int N) { iter = new IteratorN<>(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 predicte 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()) ; } /** Iterator that only returns upto N items */ static class IteratorN implements Iterator { private final Iterator iter ; private final int N ; private int count ; IteratorN(Iterator iter, int N) { this.iter = iter ; this.N = N ; this.count = 0 ; } @Override public boolean hasNext() { if ( count >= N ) return false ; return iter.hasNext() ; } @Override public T next() { if ( count >= N ) throw new NoSuchElementException() ; T x = iter.next() ; count++ ; return x ; } @Override public void remove() { // But leave the count as-is. iter.remove() ; } } /** Count the iterator (this is destructive on the iterator) */ public static long count(Iterator iterator) { long x = 0 ; while (iterator.hasNext()) { iterator.next() ; x++ ; } return x ; } /** Consume the iterator */ public static void consume(Iterator iterator) { count(iterator) ; } // ---- String related helpers // Java8 has StringJoin public static String asString(Iterable stream) { return asString(stream, new AccString()) ; } public static String asString(Iterator stream) { return asString(stream, new AccString()) ; } public static String asString(Iterable stream, String sep) { return asString(stream, new AccString(sep)) ; } public static String asString(Iterator stream, String sep) { return asString(stream, new AccString(sep)) ; } public static String asString(Iterable stream, AccString formatter) { return asString(stream.iterator(), formatter) ; } public static String asString(Iterator stream, AccString formatter) { return reduce(stream, formatter) ; } // ---- public static void close(Iterator iter) { if ( iter instanceof Closeable ) ((Closeable)iter).close() ; } /** * 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. 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(final PrintStream out, Iterator stream) { apply(stream, out::println) ; } /** Send the elements of the iterator to a sink - consumes the iterator */ public static void sendToSink(Iterator iter, Sink sink) { while ( iter.hasNext() ) { T thing = iter.next() ; sink.send(thing) ; } sink.close() ; } /** Send the elements of the iterable to a sink */ public static void sendToSink(Iterable stream, Sink sink) { sendToSink(stream.iterator(), sink) ; } // ---- // 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) { if ( iterator instanceof Iter ) return (Iter)iterator ; 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 Iterator concat(Iterator iter1, Iterator iter2) { if ( iter1 == null ) return iter2 ; if ( iter2 == null ) return 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 ; } /** @deprecated Use Java8 Streams */ @Deprecated public static T first(Collection collection, Predicate filter) { return collection.stream().filter(filter).findFirst().orElse(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 ; } /** @deprecated Use Java8 Streams */ @Deprecated public static int firstIndex(Collection collection, Predicate filter) { return firstIndex(collection.iterator(), filter) ; } /** 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 filter) { T thing = null ; while (iter.hasNext()) { T t = iter.next() ; if ( filter.test(t) ) thing = t ; } return thing ; } /** @deprecated Use Java8 Streams */ @Deprecated public static T last(Collection collection, Predicate filter) { return last(collection.iterator(), filter) ; } /** Return the index of the last element satisfying a predicate (zero-based). */ public static int lastIndex(Iterator iter, Predicate filter) { int location = -1 ; for (int idx = 0; iter.hasNext(); idx++) { T t = iter.next() ; if ( filter.test(t) ) location = idx ; } return location ; } /** @deprecated Use Java8 Streams */ @Deprecated public static int lastIndex(Collection collection, Predicate filter) { return lastIndex(collection.iterator(), filter) ; } // ------------------------------------------------------ // The class. private Iterator iterator ; private Iter(Iterator iterator) { this.iterator = iterator ; } /** 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 filter) { return first(iterator, filter) ; } /** Skip to the first element meeting a condition and return that element's index (zero-based). */ public int firstIndex(Predicate filter) { return firstIndex(iterator, filter) ; } /** Return the last element satisfying a predicate. This operation destroys the whole iterator. */ public T last(Predicate filter) { return last(iterator, filter) ; } /** Return the index of the last element satisfying a predicate (zero-based). */ public int lastIndex(Predicate filter) { return lastIndex(iterator, filter) ; } /** Filter by predicate */ public Iter filter(Predicate filter) { return iter(filter(iterator, filter)) ; } /** Return true if every element satisfies a predicate */ public boolean every(Predicate predciate) { return every(iterator, predciate) ; } /** Return true if some element satisfies a predicate */ public boolean some(Predicate filter) { return some(iterator, filter) ; } /** Remove nulls */ public Iter removeNulls() { return iter(removeNulls(this)) ; } /** Map each element using given function */ public Iter map(Function converter) { return iter(map(iterator, converter)) ; } /** * Apply an action to everything in the stream, yielding a stream of the * original items. */ public Iter operate(Consumer action) { return iter(operate(iterator, action)) ; } /** Reduce by aggregator. * This reduce is fold-left (take first element, apply to rest of list) */ public R reduce(Accumulate aggregator) { return reduce(iterator, aggregator) ; } /** Apply an action to every element of an iterator */ public void apply(Consumer action) { apply(iterator, action) ; } /** Join on an {@code Iterator}.. * If there are going to be many iterators, uit is better to create an {@link IteratorConcat} * and .add each iterator. The overheads are much lower. */ public Iter append(Iterator iter) { return iter(IteratorCons.create(iterator, iter)) ; } /** Return an Iter that yields at most the first N items */ public Iter take(int N) { return iter(take(iterator, N)) ; } /** Create an {@code Iter} such that it yields elements while a predicate test on * the elements is true, end the iteration. * @see Iter#filter(Predicate) */ public Iter takeWhile(Predicate predicate) { return iter(takeWhile(iterator, predicate)) ; } /** * Create an {@code Iter} such that it yields elements until a predicate test on * the elements becomes true, end the iteration. * * @see Iter#filter(Predicate) */ public Iter takeUntil(Predicate predicate) { return iter(takeUntil(iterator, predicate)) ; } /** Create an {@code Iter} 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 predicte becomes true is the first element of the * returned iterator. */ public Iter dropWhile(Predicate predicate) { return iter(dropWhile(iterator, predicate)) ; } /** Create an {@code Iter} 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 Iter dropUntil(Predicate predicate) { return iter(dropWhile(iterator, predicate.negate())) ; } /** Count the iterator (this is destructive on the iterator) */ public long count() { ActionCount action = new ActionCount<>() ; apply(action) ; return action.getCount() ; } public String asString() { return asString(iterator) ; } public String asString(String sep) { return asString(iterator, sep) ; } /** Return an {:@code Iter} that will see each element of the underlying iterator only once. * Note that this need working memory to remember the elements alreadey seen. */ public Iter distinct() { return iter((distinct(iterator))) ; } /** 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 Iter distinctAdjacent() { return iter(distinctAdjacent(iterator)) ; } // ---- Iterator @Override public boolean hasNext() { return iterator.hasNext() ; } @Override public T next() { return iterator.next() ; } @Override public void remove() { iterator.remove() ; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy