org.neo4j.internal.helpers.collection.Iterators Maven / Gradle / Ivy
Show all versions of neo4j-collections Show documentation
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [https://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.internal.helpers.collection;
import static java.util.Collections.emptyIterator;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.StringJoiner;
import java.util.TreeSet;
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.eclipse.collections.api.iterator.LongIterator;
import org.neo4j.collection.RawIterator;
import org.neo4j.function.Predicates;
import org.neo4j.function.ThrowingFunction;
import org.neo4j.graphdb.Resource;
import org.neo4j.graphdb.ResourceIterable;
import org.neo4j.graphdb.ResourceIterator;
/**
* Contains common functionality regarding {@link Iterator}s and
* {@link Iterable}s.
*/
public final class Iterators {
private Iterators() {
throw new AssertionError("no instance");
}
/**
* Returns the given iterator's first element or {@code null} if no
* element found.
*
* @param the type of elements in {@code iterator}.
* @param iterator the {@link Iterator} to get elements from.
* @return the first element in the {@code iterator}, or {@code null} if no
* element found.
*/
public static T firstOrNull(Iterator iterator) {
try {
return iterator.hasNext() ? iterator.next() : null;
} finally {
tryCloseResource(iterator);
}
}
/**
* Returns the given iterator's first element or {@param defaultValue} if no
* element found.
*
* @param the type of elements in {@code iterator}.
* @param iterator the {@link Iterator} to get elements from.
* @param defaultValue the default value to return.
* @return the first element in the {@code iterator}, or {@code null} if no
* element found.
*/
public static T firstOrDefault(Iterator iterator, T defaultValue) {
try {
return iterator.hasNext() ? iterator.next() : defaultValue;
} finally {
tryCloseResource(iterator);
}
}
/**
* Returns the given iterator's first element. If no element is found a
* {@link NoSuchElementException} is thrown.
*
* @param the type of elements in {@code iterator}.
* @param iterator the {@link Iterator} to get elements from.
* @return the first element in the {@code iterator}.
* @throws NoSuchElementException if no element found.
*/
public static T first(Iterator iterator) {
return assertNotNull(iterator, firstOrNull(iterator));
}
/**
* Returns the given iterator's last element or {@code null} if no
* element found.
*
* @param the type of elements in {@code iterator}.
* @param iterator the {@link Iterator} to get elements from.
* @return the last element in the {@code iterator}, or {@code null} if no
* element found.
*/
static T lastOrNull(Iterator iterator) {
try {
T result = null;
while (iterator.hasNext()) {
result = iterator.next();
}
return result;
} finally {
tryCloseResource(iterator);
}
}
/**
* Returns the given iterator's last element. If no element is found a
* {@link NoSuchElementException} is thrown.
*
* @param the type of elements in {@code iterator}.
* @param iterator the {@link Iterator} to get elements from.
* @return the last element in the {@code iterator}.
* @throws NoSuchElementException if no element found.
*/
public static T last(Iterator iterator) {
return assertNotNull(iterator, lastOrNull(iterator));
}
/**
* Returns the given iterator's single element or {@code null} if no
* element found. If there is more than one element in the iterator a
* {@link NoSuchElementException} will be thrown.
*
* If the {@code iterator} implements {@link Resource} it will be {@link Resource#close() closed}
* in a {@code finally} block after the single item has been retrieved, or failed to be retrieved.
*
* @param the type of elements in {@code iterator}.
* @param iterator the {@link Iterator} to get elements from.
* @return the single element in {@code iterator}, or {@code null} if no
* element found.
* @throws NoSuchElementException if more than one element was found.
*/
public static T singleOrNull(Iterator iterator) {
try {
return single(iterator, null);
} finally {
tryCloseResource(iterator);
}
}
/**
* Returns the given iterator's single element. If there are no elements
* or more than one element in the iterator a {@link NoSuchElementException}
* will be thrown.
*
* If the {@code iterator} implements {@link Resource} it will be {@link Resource#close() closed}
* in a {@code finally} block after the single item has been retrieved, or failed to be retrieved.
*
* @param the type of elements in {@code iterator}.
* @param iterator the {@link Iterator} to get elements from.
* @return the single element in the {@code iterator}.
* @throws NoSuchElementException if there isn't exactly one element.
*/
public static T single(Iterator iterator) {
return assertNotNull(iterator, singleOrNull(iterator));
}
/**
* Returns the iterator's n:th item from the end of the iteration.
* If the iterator has got less than n-1 items in it
* {@link NoSuchElementException} is thrown.
*
* @param the type of elements in {@code iterator}.
* @param iterator the {@link Iterator} to get elements from.
* @param n the n:th item from the end to get.
* @return the iterator's n:th item from the end of the iteration.
* @throws NoSuchElementException if the iterator contains less than n-1 items.
*/
static T fromEnd(Iterator iterator, int n) {
return assertNotNull(iterator, fromEndOrNull(iterator, n));
}
/**
* Returns the iterator's n:th item from the end of the iteration.
* If the iterator has got less than n-1 items in it {@code null} is returned.
*
* @param the type of elements in {@code iterator}.
* @param iterator the {@link Iterator} to get elements from.
* @param n the n:th item from the end to get.
* @return the iterator's n:th item from the end of the iteration,
* or {@code null} if the iterator doesn't contain that many items.
*/
private static T fromEndOrNull(Iterator iterator, int n) {
try {
Deque trail = new ArrayDeque<>(n);
while (iterator.hasNext()) {
if (trail.size() > n) {
trail.removeLast();
}
trail.addFirst(iterator.next());
}
return trail.size() == n + 1 ? trail.getLast() : null;
} finally {
tryCloseResource(iterator);
}
}
/**
* Iterates over the full iterators, and checks equality for each item in them. Note that this
* will consume the iterators.
*
* @param first the first iterator
* @param other the other iterator
* @return {@code true} if all items are equal; otherwise
*/
public static boolean iteratorsEqual(Iterator> first, Iterator> other) {
try {
while (first.hasNext() && other.hasNext()) {
if (!Objects.equals(first.next(), other.next())) {
return false;
}
}
return first.hasNext() == other.hasNext();
} finally {
tryCloseResource(first);
tryCloseResource(other);
}
}
private static T assertNotNull(Iterator iterator, T result) {
if (result == null) {
throw new NoSuchElementException("No element found in " + iterator);
}
return result;
}
/**
* Returns the given iterator's single element or {@code itemIfNone} if no
* element found. If there is more than one element in the iterator a
* {@link NoSuchElementException} will be thrown.
*
* If the {@code iterator} implements {@link Resource} it will be {@link Resource#close() closed}
* in a {@code finally} block after the single item has been retrieved, or failed to be retrieved.
*
* @param the type of elements in {@code iterator}.
* @param iterator the {@link Iterator} to get elements from.
* @param itemIfNone item to use if none is found
* @return the single element in {@code iterator}, or {@code itemIfNone} if no
* element found.
* @throws NoSuchElementException if more than one element was found.
*/
public static T single(Iterator iterator, T itemIfNone) {
try {
T result = iterator.hasNext() ? iterator.next() : itemIfNone;
if (iterator.hasNext()) {
throw new NoSuchElementException("More than one element in " + iterator + ". First element is '"
+ result + "' and the second element is '" + iterator.next() + "'");
}
return result;
} finally {
tryCloseResource(iterator);
}
}
/**
* Adds all the items in {@code iterator} to {@code collection}.
* @param the type of {@link Collection} to add to items to.
* @param the type of items in the collection and iterator.
* @param iterator the {@link Iterator} to grab the items from.
* @param collection the {@link Collection} to add the items to.
* @return the {@code collection} which was passed in, now filled
* with the items from {@code iterator}.
*/
public static , T> C addToCollection(Iterator iterator, C collection) {
try {
while (iterator.hasNext()) {
collection.add(iterator.next());
}
return collection;
} finally {
tryCloseResource(iterator);
}
}
/**
* Adds all the items in {@code iterator} to {@code collection}.
* @param the type of {@link Collection} to add to items to.
* @param the type of items in the collection and iterator.
* @param iterator the {@link Iterator} to grab the items from.
* @param collection the {@link Collection} to add the items to.
* @return the {@code collection} which was passed in, now filled
* with the items from {@code iterator}.
*/
private static , T> C addToCollectionUnique(Iterator iterator, C collection) {
try {
while (iterator.hasNext()) {
addUnique(collection, iterator.next());
}
return collection;
} finally {
tryCloseResource(iterator);
}
}
private static > void addUnique(C collection, T item) {
if (!collection.add(item)) {
throw new IllegalStateException("Encountered an already added item:" + item
+ " when adding items uniquely to a collection:" + collection);
}
}
/**
* Adds all the items in {@code iterator} to {@code collection}.
* @param the type of {@link Collection} to add to items to.
* @param the type of items in the collection and iterator.
* @param iterable the {@link Iterator} to grab the items from.
* @param collection the {@link Collection} to add the items to.
* @return the {@code collection} which was passed in, now filled
* with the items from {@code iterator}.
*/
static , T> C addToCollectionUnique(Iterable iterable, C collection) {
return addToCollectionUnique(iterable.iterator(), collection);
}
/**
* Convenience method for looping over an {@link Iterator}. Converts the
* {@link Iterator} to an {@link Iterable} by wrapping it in an
* {@link Iterable} that returns the {@link Iterator}. It breaks the
* contract of {@link Iterable} in that it returns the supplied iterator
* instance for each call to {@code iterator()} on the returned
* {@link Iterable} instance. This method exists to make it easy to use an
* {@link Iterator} in a for-loop.
*
* @param the type of items in the iterator.
* @param iterator the iterator to expose as an {@link Iterable}.
* @return the supplied iterator posing as an {@link Iterable}.
*/
public static Iterable loop(final Iterator iterator) {
return () -> iterator;
}
/**
* Counts the number of items in the {@code iterator} by looping
* through it.
*
* If the {@code iterator} implements {@link Resource} it will be {@link Resource#close() closed}
* in a {@code finally} block after the items have been counted.
*
* @param the type of items in the iterator.
* @param iterator the {@link Iterator} to count items in.
* @return the number of items found in {@code iterator}.
*/
public static long count(Iterator iterator) {
return count(iterator, Predicates.alwaysTrue());
}
/**
* Counts the number of filtered in the {@code iterator} by looping
* through it.
*
* If the {@code iterator} implements {@link Resource} it will be {@link Resource#close() closed}
* in a {@code finally} block after the items have been counted.
*
* @param the type of items in the iterator.
* @param iterator the {@link Iterator} to count items in.
* @param filter the filter to test items against
* @return the number of filtered items found in {@code iterator}.
*/
public static long count(Iterator iterator, Predicate filter) {
try {
long result = 0;
while (iterator.hasNext()) {
if (filter.test(iterator.next())) {
result++;
}
}
return result;
} finally {
tryCloseResource(iterator);
}
}
public static Collection asCollection(Iterator iterable) {
return addToCollection(iterable, new ArrayList<>());
}
public static List asList(Iterator iterator) {
return addToCollection(iterator, new ArrayList<>());
}
public static List asList(RawIterator iterator) throws EX {
try {
List out = new ArrayList<>();
while (iterator.hasNext()) {
out.add(iterator.next());
}
return out;
} finally {
tryCloseResource(iterator);
}
}
public static Set asSet(Iterator iterator) {
return addToCollection(iterator, new HashSet<>());
}
/**
* Creates a {@link Set} from an array of items.an
*
* @param items the items to add to the set.
* @param the type of the items
* @return the {@link Set} containing the items.
*/
@SafeVarargs
public static Set asSet(T... items) {
return new HashSet<>(Arrays.asList(items));
}
/**
* Alias for asSet()
*
* @param items the items to add to the set.
* @param the type of the items
* @return the {@link Set} containing the items.
*/
@SafeVarargs
public static Set set(T... items) {
return asSet(items);
}
/**
* Creates a {@link Set} from an array of items.
*
* @param items the items to add to the set.
* @param the type of the items
* @return the {@link Set} containing the items.
*/
@SafeVarargs
public static Set asUniqueSet(T... items) {
Set set = new HashSet<>();
for (T item : items) {
addUnique(set, item);
}
return set;
}
/**
* Creates a {@link Set} from an array of items.
*
* @param iterator the items to add to the set.
* @param the type of the items
* @return the {@link Set} containing the items.
*/
public static Set asUniqueSet(Iterator iterator) {
try {
Set set = new HashSet<>();
while (iterator.hasNext()) {
addUnique(set, iterator.next());
}
return set;
} finally {
tryCloseResource(iterator);
}
}
public static SortedSet asSortedSet(Comparator comparator, T... items) {
SortedSet set = new TreeSet<>(comparator);
Collections.addAll(set, items);
return set;
}
static Iterator asIterator(final long... array) {
return new Iterator<>() {
private int index;
@Override
public boolean hasNext() {
return index < array.length;
}
@Override
public Long next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return array[index++];
}
};
}
public static Iterator asIterator(final int... array) {
return new Iterator<>() {
private int index;
@Override
public boolean hasNext() {
return index < array.length;
}
@Override
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return array[index++];
}
};
}
@SafeVarargs
private static Iterator asIterator(final int maxItems, final T... array) {
return new Iterator<>() {
int index;
@Override
public boolean hasNext() {
return index < maxItems;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return array[index++];
}
};
}
public static Iterator iterator(final T item) {
if (item == null) {
return emptyIterator();
}
return new Iterator<>() {
T myItem = item;
@Override
public boolean hasNext() {
return myItem != null;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
T toReturn = myItem;
myItem = null;
return toReturn;
}
};
}
@SafeVarargs
public static Iterator iterator(T... items) {
return asIterator(items.length, items);
}
@SafeVarargs
public static Iterator iterator(int maxItems, T... items) {
return asIterator(maxItems, items);
}
public static Iterator appendTo(Iterator iterator, T... appended) {
return new Iterator<>() {
private int index;
@Override
public boolean hasNext() {
return iterator.hasNext() || index < appended.length;
}
@Override
public T next() {
if (iterator.hasNext()) {
return iterator.next();
} else if (index < appended.length) {
return appended[index++];
} else {
tryCloseResource(iterator);
throw new NoSuchElementException();
}
}
};
}
public static Iterator prependTo(Iterator iterator, T... prepended) {
return new Iterator<>() {
private int index;
@Override
public boolean hasNext() {
return index < prepended.length || iterator.hasNext();
}
@Override
public T next() {
if (index < prepended.length) {
return prepended[index++];
} else if (iterator.hasNext()) {
return iterator.next();
} else {
tryCloseResource(iterator);
throw new NoSuchElementException();
}
}
};
}
/**
* Produce a string of the form {@code [A, B, C, ...]} by consuming up to {@code maxItems} objects from the iterator,
* optionally appending an ellipsis if the iterator has more than {@code maxItems} in it.
*
* @param iterator the iterator to format as a string.
* @param toString the function that converts the iterator elements to strings.
* @param maxItems the maximum number of items to include in the string.
* @param the type of objects in the iterator.
* @return a string of the form {@code [A, B, C]} or {@code [A, B, C, ...]}.
*/
public static String toString(Iterator iterator, Function toString, int maxItems) {
try {
StringJoiner joiner = new StringJoiner(", ", "[", "]");
while (iterator.hasNext() && maxItems > 0) {
String str = toString.apply(iterator.next());
joiner.add(str);
maxItems--;
}
if (iterator.hasNext()) {
joiner.add("...");
}
return joiner.toString();
} finally {
tryCloseResource(iterator);
}
}
@SuppressWarnings("unchecked")
public static ResourceIterator emptyResourceIterator() {
return (ResourceIterator) EmptyResourceIterator.EMPTY_RESOURCE_ITERATOR;
}
public static ResourceIterator asResourceIterator(final Iterable iterable) {
if (iterable instanceof ResourceIterable) {
return ((ResourceIterable) iterable).iterator();
}
return asResourceIterator(iterable.iterator());
}
public static ResourceIterator asResourceIterator(final Iterator iterator) {
if (iterator instanceof ResourceIterator>) {
return (ResourceIterator) iterator;
}
return new WrappingResourceIterator<>(iterator);
}
public static ResourceIterator resourceIterator(final Iterator iterator, final Resource resource) {
return new PrefetchingResourceIterator<>() {
@Override
public void close() {
resource.close();
}
@Override
protected T fetchNextOrNull() {
return iterator.hasNext() ? iterator.next() : null;
}
};
}
@SafeVarargs
public static T[] array(T... items) {
return items;
}
public static Iterator filter(Predicate super X> specification, Iterator i) {
return new FilterIterable.FilterIterator<>(i, specification);
}
public static Iterator map(Function super FROM, ? extends TO> function, Iterator from) {
return new MapIterable.MapIterator<>(from, function);
}
public static RawIterator map(
ThrowingFunction super FROM, ? extends TO, EX> function, RawIterator from) {
return new RawMapIterator<>(from, function);
}
public static RawIterator asRawIterator(Iterator iter) {
return new RawIterator<>() {
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public T next() {
return iter.next();
}
};
}
public static RawIterator asRawIterator(Stream stream) {
return asRawIterator(stream.iterator());
}
@SafeVarargs
@SuppressWarnings("unchecked")
public static Iterator concat(Iterator extends T>... iterators) {
return concat(Arrays.asList((Iterator[]) iterators).iterator());
}
public static ResourceIterator concatResourceIterators(Iterator> iterators) {
return new CombiningResourceIterator<>(iterators);
}
public static Iterator concat(Iterator> iterators) {
return new CombiningIterator<>(iterators);
}
public static PrefetchingIterator prefetching(Iterator iterator) {
return iterator instanceof PrefetchingIterator
? (PrefetchingIterator) iterator
: new PrefetchingIterator<>() {
@Override
protected T fetchNextOrNull() {
return iterator.hasNext() ? iterator.next() : null;
}
};
}
/**
* Create a stream from the given iterator.
*
* Note: returned stream needs to be closed via {@link Stream#close()} if the given iterator implements
* {@link Resource}.
*
* @param iterator the iterator to convert to stream
* @param the type of elements in the given iterator
* @return stream over the iterator elements
* @throws NullPointerException when the given stream is {@code null}
*/
public static Stream stream(Iterator iterator) {
return stream(iterator, 0);
}
/**
* Create a stream from the given iterator with given characteristics.
*
* Note: returned stream needs to be closed via {@link Stream#close()} if the given iterator implements
* {@link Resource}.
*
* @param iterator the iterator to convert to stream
* @param characteristics the logical OR of characteristics for the underlying {@link Spliterator}
* @param the type of elements in the given iterator
* @return stream over the iterator elements
* @throws NullPointerException when the given iterator is {@code null}
*/
public static Stream stream(Iterator iterator, int characteristics) {
Objects.requireNonNull(iterator);
Spliterator spliterator = Spliterators.spliteratorUnknownSize(iterator, characteristics);
Stream stream = StreamSupport.stream(spliterator, false);
if (iterator instanceof Resource resource) {
return stream.onClose(resource::close);
}
return stream;
}
/**
* Process each remaining item in the iterator
*
* Note: the iterator will be closed via {@link Resource#close()} if the given iterator implements
* {@link Resource}.
*
* @param iterator the iterator to process
* @param consumer the consumer of each remaining item in the iterator
* @param the type of elements in the given iterator
*/
public static void forEachRemaining(Iterator iterator, Consumer super T> consumer) {
try {
iterator.forEachRemaining(consumer);
} finally {
tryCloseResource(iterator);
}
}
/**
* Skips the first items from the iterator
*
* @param iterator the iterator to skip items in
* @param count number of items to skip
*/
public static void skip(Iterator iterator, long count) {
for (; count > 0 && iterator.hasNext(); count--) {
iterator.next();
}
}
/**
* Skips the first items from the iterator
*
* @param iterator the iterator to skip items in
* @param count number of items to skip
*/
public static void skip(LongIterator iterator, long count) {
for (; count > 0 && iterator.hasNext(); count--) {
iterator.next();
}
}
/**
* Close the provided {@code iterator} if it implements {@link Resource}.
*
* @param iterator the iterator to check for closing
*/
public static void tryCloseResource(Iterator> iterator) {
if (iterator instanceof Resource closeable) {
closeable.close();
}
}
private static void tryCloseResource(RawIterator, ?> iterator) {
if (iterator instanceof Resource closeable) {
closeable.close();
}
}
private static class EmptyResourceIterator implements ResourceIterator {
private static final ResourceIterator