Iterables.java
package org.codefilarete.tool.collection;
import javax.annotation.Nullable;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.Spliterator;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.codefilarete.tool.Duo;
import org.codefilarete.tool.collection.PairIterator.UntilBothIterator;
import org.codefilarete.tool.trace.MutableInt;
/**
* @author Guillaume Mary
*/
public final class Iterables {
/**
* Transforms an {@link Iterator} to an {@link Iterable}
*
* @param iterator the delegate
* @param <E> the content typed of the {@link Iterator}
* @return an {@link Iterable} which {@link Iterable#iterator()} returns the given {@link Iterator}
*/
public static <E> Iterable<E> asIterable(Iterator<E> iterator) {
return () -> iterator;
}
/**
* Iterates over the given {@link Iterable} by chunks of <code>chunkSize</code> size.
* This method is made to avoid boilerplate code that wraps "batch treatments"; thus, each chunk is given to the <code>consumer</code> argument,
* even the very last one even if it is not of <code>chunkSize</code>.
* Hence, <code>beforeConsumer</code> is called only once before iterating over all chunks, because it is expected to prepare a
* "context" that is passed back to the chunk consumer.
* As well, <code>afterConsumer</code> is called only once after all chunks iteration, because it is expected to clean this "context".
*
* @param iterable the iterable to be iterated over by partition of chunkSize
* @param beforeAllConsumer code to be invoked before the treatment of all chunks
* @param chunkSize the size of the chunk to be passed to the consumer
* @param beforeConsumer code to be invoked once before all chunks that have chunkSize size, and the remaining one
* @param consumer the consumer to invoke with each chunk
* @param afterConsumer code to be invoked once after all chunks that have chunkSize size, and the remaining one
* @param <E> the iterable element type
* @param <O> the context type
*/
public static <E, O> void forEachChunk(Iterable<E> iterable,
int chunkSize,
Consumer<List<List<E>>> beforeAllConsumer,
Function<Integer, O> beforeConsumer,
BiConsumer<O, ? super List<E>> consumer,
Consumer<O> afterConsumer) {
List<List<E>> chunks = chunk(iterable, chunkSize);
if (!chunks.isEmpty()) {
beforeAllConsumer.accept(chunks);
List<E> lastChunk = last(chunks, java.util.Collections.emptyList());
if (lastChunk.size() != chunkSize) {
chunks = Iterables.cutTail(chunks);
} else {
lastChunk = java.util.Collections.emptyList();
}
if (!chunks.isEmpty()) {
O initObject = beforeConsumer.apply(chunkSize);
chunks.forEach(chunk -> consumer.accept(initObject, chunk));
afterConsumer.accept(initObject);
}
// last packet treatment (chunk size may be different)
if (!lastChunk.isEmpty()) {
O initObject = beforeConsumer.apply(lastChunk.size());
consumer.accept(initObject, lastChunk);
afterConsumer.accept(initObject);
}
}
}
/**
* Gives the size of the given {@link Iterable}.
* Throws an exception if its size can't be known (if it is not a {@link Collection} and its {@link Spliterator} doesn't have a
* {@link Spliterator#getExactSizeIfKnown()}).
*
* @param iterable the object of which we need the size
* @return the size of the given {@link Iterable}
*/
public static int size(Iterable<?> iterable) {
return size(iterable, () -> {
throw new UnsupportedOperationException("Can't give size of Iterable, make it override Spliterator.getExactSizeIfKnown() or support Spliterator.SIZED");
});
}
/**
* Gives the size of the given {@link Iterable} or returns a default size if its size can't be known (if it is not a {@link Collection} and
* its {@link Spliterator} doesn't have a {@link Spliterator#getExactSizeIfKnown()}).
*
* @param iterable the object of which we need the size
* @param defaultSizeSupplier supplier of a the value to return if the size can't be computed
* @return the size of the given {@link Iterable}
*/
public static int size(Iterable<?> iterable, Supplier<Integer> defaultSizeSupplier) {
if (iterable instanceof Collection) {
return ((Collection<?>) iterable).size();
} else {
Spliterator<?> spliterator = iterable.spliterator();
int exactSizeIfKnown = (int) spliterator.getExactSizeIfKnown();
if (exactSizeIfKnown == -1) {
return defaultSizeSupplier.get();
} else {
return exactSizeIfKnown;
}
}
}
/**
* @param iterable a nullable {@link Iterable}
* @return the first element of the argument or null if argument is empty
*/
@Nullable
public static <E> E first(@Nullable Iterable<E> iterable) {
return first(iterable, null);
}
/**
* @param iterable a nullable {@link Iterable}
* @return the first element of the argument or the given default value if argument is empty
*/
public static <E> E first(@Nullable Iterable<E> iterable, E defaultValue) {
if (iterable == null) {
return defaultValue;
} else {
return first(iterable.iterator());
}
}
/**
* @param iterator a nullable {@link Iterator}
* @return the first element of the argument or null if argument is empty
*/
@Nullable
public static <E> E first(Iterator<E> iterator) {
return first(iterator, null);
}
/**
* @param iterator a nullable {@link Iterator}
* @return the first element of the argument or the given default value if argument is empty
*/
public static <E> E first(Iterator<E> iterator, E defaultValue) {
return iterator != null && iterator.hasNext() ? iterator.next() : defaultValue;
}
/**
* Optimized version of {@link #first(Iterable)} for {@link List} : will use {@code get(0)} on the list to get first element (instead of hasNext())
*
* @param iterable a nullable {@link List}
* @return the first element of the argument or null if argument is empty
*/
@Nullable
public static <E> E first(List<E> iterable) {
return first(iterable, null);
}
/**
* Optimized version of {@link #first(Iterable)} for {@link List} : will use {@code get(0)} on the list to get first element (instead of hasNext())
*
* @param iterable a nullable {@link List}
* @return the first element of the argument or the given default value if argument is empty
*/
public static <E> E first(List<E> iterable, E defaultValue) {
if (Collections.isEmpty(iterable)) {
return defaultValue;
} else {
return iterable.get(0);
}
}
/**
* Optimized version of {@link #first(Iterable)} for arrays : will use {@code [0]} on the array to get first element (instead of hasNext())
*
* @param array a nullable array
* @return the first element of the argument or null if argument is empty
*/
@Nullable
public static <E> E first(E[] array) {
return first(array, null);
}
/**
* Optimized version of {@link #first(Iterable)} for arrays : will use {@code [0]} on the array to get first element (instead of hasNext())
*
* @param array a nullable array
* @return the first element of the argument or the given default value if argument is empty
*/
public static <E> E first(E[] array, E defaultValue) {
if (Arrays.isEmpty(array)) {
return defaultValue;
} else {
return array[0];
}
}
/**
* Returns the first entry of a {@link Map}, only makes sense on a {@link SortedMap} or a {@link LinkedHashMap}
*
* @param iterable a Map : Sorted or Linked (else it has no purpose)
* @return the first {@link Entry} of the Map, or null if argument is empty or null
*/
@Nullable
public static <K, V> Map.Entry<K, V> first(@Nullable Map<K, V> iterable) {
return first(iterable, null);
}
/**
* Returns the first entry of a {@link Map}, only makes sense on a {@link SortedMap} or a {@link LinkedHashMap}
*
* @param iterable a Map : Sorted or Linked (else it has no purpose)
* @return the first {@link Entry} of the Map, or the given default {@link Entry} if argument is empty or null
*/
public static <K, V> Map.Entry<K, V> first(@Nullable Map<K, V> iterable, Map.Entry<K, V> defaultValue) {
if (iterable == null) {
return defaultValue;
} else {
return first(iterable.entrySet());
}
}
/**
* Returns the first value of a {@link Map}, only makes sense on a {@link SortedMap} or a {@link LinkedHashMap}
*
* @param iterable a Map : Sorted or Linked (else it has no purpose)
* @return the first value of the Map, null if argument is null
*/
@Nullable
public static <V> V firstValue(@Nullable Map<?, V> iterable) {
return firstValue(iterable, null);
}
/**
* Returns the first value of a {@link Map}, only makes sense on a {@link SortedMap} or a {@link LinkedHashMap}
*
* @param iterable a Map : Sorted or Linked (else it has no purpose)
* @return the first value of the Map, or the given default value if argument is empty or null
*/
@Nullable
public static <V> V firstValue(@Nullable Map<?, V> iterable, V defaultValue) {
Entry<?, V> firstEntry = first(iterable);
return firstEntry == null ? defaultValue : firstEntry.getValue();
}
/**
* Indicates if an {@link Iterable} is empty or not.
* Implementation is optimized for {@link Collection}s by using {@link Collection#isEmpty()}, else will use {@link Iterator#hasNext()}.
*
* @param iterable a nullable {@link Iterable}
* @return true if given argument is null or has no element
*/
public static boolean isEmpty(@Nullable Iterable iterable) {
return iterable == null
|| ((iterable instanceof Collection) ? ((Collection) iterable).isEmpty() : !iterable.iterator().hasNext()) ;
}
/**
* @param iterable a nullable {@link List}
* @return the last element of the argument or null if argument is empty or null
*/
@Nullable
public static <E> E last(@Nullable List<E> iterable) {
return last(iterable, null);
}
/**
* @param iterable a nullable {@link List}
* @return the last element of the argument or the given default value if argument is empty or null
*/
public static <E> E last(@Nullable List<E> iterable, E defaultValue) {
if (iterable == null || iterable.isEmpty()) {
return defaultValue;
} else {
return iterable.get(iterable.size() - 1);
}
}
/**
* @param iterable a nullable {@link Iterable}
* @return the last element of the argument or null
*/
public static <E> E last(@Nullable Iterable<E> iterable) {
return last(iterable, null);
}
/**
* @param iterable a nullable {@link Iterable}
* @param defaultValue value to be return if iterable is empty
* @return the last element of the argument or the given default value if argument is empty or null
*/
public static <E> E last(@Nullable Iterable<E> iterable, @Nullable E defaultValue) {
if (iterable == null) {
return defaultValue;
} else {
Iterator<E> iterator = iterable.iterator();
E result = null;
if (!iterator.hasNext()) {
result = defaultValue;
}
while (iterator.hasNext()) {
result = iterator.next();
}
return result;
}
}
/**
* Collects some elements from an iterable until a given element
*
* @param iterable the iterable to scan
* @param untilExcluded stopping element
* @param <E> elements type
* @return firsts elements of the iterable that differ from {@code untilExcluded}, in the iteration order, without {@code untilExcluded}
*/
public static <E> List<E> head(Iterable<E> iterable, E untilExcluded) {
List<E> result = new ArrayList<>();
for (E e : iterable) {
if (untilExcluded.equals(e)) {
break;
} else {
result.add(e);
}
}
return result;
}
/**
* Collects the <code>count</code> first elements of given {@link List}.
* Shortcut for {@link List#subList(int, int)}
*
* @param iterable the iterable to extract elements from
* @param count number of elements to be collected
* @param <E> elements type
* @return firsts elements of given iterable
*/
public static <E> List<E> head(List<E> iterable, int count) {
return new ArrayList<>(iterable.subList(0, count));
}
/**
* Returns the head of given {@link List} without its last element.
* Returns a copy of given argument
*
* @param list the {@link List} to extract elements from
* @return a copy of given argument without its very last element
* @param <E> element type
* @see #cutHead(List)
*/
public static <E> List<E> cutTail(List<E> list) {
return cutTail(list, 1);
}
/**
* Returns the head of given {@link List}, without its last <code>elementCount</code> elements.
* Returns a copy of given argument
*
* @param list the {@link List} to extract elements from
* @return a copy of given argument without its last elements
* @param <E> element type
* @see #cutHead(List, int)
*/
public static <E> List<E> cutTail(List<E> list, int elementCount) {
return new ArrayList<>(list.subList(0, list.size() - elementCount));
}
/**
* Returns the tail of given {@link List} without its first element.
* Returns a copy of given argument
*
* @param list the {@link List} to extract elements from
* @return a copy of given argument without its very first element
* @param <E> element type
* @see #cutTail(List)}
*/
public static <E> List<E> cutHead(List<E> list) {
return cutHead(list, 1);
}
/**
* Returns the tail of given {@link List}, without its first <code>elementCount</code> elements.
* Returns a copy of given argument
*
* @param list the {@link List} to extract elements from
* @return a copy of given argument without its first elements
* @param <E> element type
* @see #cutTail(List, int)}
*/
public static <E> List<E> cutHead(List<E> list, int elementCount) {
return new ArrayList<>(list.subList(elementCount, list.size()));
}
/**
* Maps an {@link Iterable} on a key took as a {@link Function} of its beans. The value is also a function took on them.
*
* @param iterable the iterable to map
* @param keyMapper the key provider
* @param valueMapper the value provider
* @param <T> iterated objets type
* @param <K> keys type
* @param <V> values type
* @return a new (Hash)Map
*/
public static <T, K, V> Map<K, V> map(Iterable<T> iterable,
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper) {
return map(iterable, keyMapper, valueMapper, HashMap::new);
}
/**
* Maps an {@link Iterable} on a key took as a {@link Function} of its beans. The value is also a function took on them.
*
* @param iterable the iterable to map
* @param keyMapper the key provider
* @param valueMapper the value provider
* @param target the map to be filled
* @param <T> iterated objets type
* @param <K> keys type
* @param <V> values type
* @param <M> returned {@link Map} type
* @return a new {@link Map} of type M filled with key and values from given parameters
*/
public static <T, K, V, M extends Map<K, V>> M map(Iterable<T> iterable,
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper,
Supplier<M> target) {
M result = target.get();
for (T t : iterable) {
result.put(keyMapper.apply(t), valueMapper.apply(t));
}
return result;
}
/**
* Maps objects of an {@link Iterable} over a key took as a {@link Function} of them.
*
* @param iterable the iterable to map
* @param keyMapper the key provider
* @param <T> iterated objets type
* @param <K> keys type
* @return a new (Hash)Map
*/
public static <T, K> Map<K, T> map(Iterable<T> iterable, Function<? super T, ? extends K> keyMapper) {
return map(iterable, keyMapper, Function.identity());
}
/**
* Maps an {@link Iterable} on a key took as a {@link Function} of its beans. The value is also a function took on them.
*
* @param iterable the iterable to map
* @param keyMapper the key provider
* @param target the map to be filled
* @param <T> iterated objets type
* @param <K> keys type
* @param <M> returned {@link Map} type
* @return a new {@link Map} of type M filled with key and values from given parameters
*/
public static <T, K, M extends Map<K, T>> M map(Iterable<T> iterable, Function<? super T, ? extends K> keyMapper, Supplier<M> target) {
return map(iterable, keyMapper, Function.identity(), target);
}
/**
* Equivalent to {@link #collect(Iterable, Function, Supplier)} collecting to a {@link List}
*
* @param iterable the source
* @param mapper the mapping function
* @param <I> input type
* @param <O> output type
* @return the collection given by the supplier
*/
public static <I, O> List<O> collectToList(Iterable<? extends I> iterable, Function<I, O> mapper) {
return collect(iterable, mapper, ArrayList::new);
}
/**
* Returns an {@link Iterable} as a {@link List}: return it if it's one, else creates a new one from it.
*
* @param c an {@link Iterable}
* @param <E> contained element type
* @return a new {@link List} or the Collection if it's one
*/
public static <E> List<E> asList(Iterable<E> c) {
return c instanceof List ? (List<E>) c : copy(c);
}
/**
* Applies a mapper over an {@link Iterable} and puts each object into a collection.
*
* @param iterable the source
* @param mapper the mapping function
* @param target the supplier of resulting collection
* @param <I> the input type
* @param <O> the output type
* @param <C> the collecting type
* @return the collection given by the supplier
*/
public static <I, O, C extends Collection<O>> C collect(Iterable<? extends I> iterable, Function<I, O> mapper, Supplier<C> target) {
return collect(iterable, i -> true, mapper, target);
}
/**
* Applies a filter and a mapper over an {@link Iterable} to collect objects and puts them into a collection.
*
* @param iterable the source
* @param acceptFilter the accepting condition
* @param mapper the mapping function
* @param target the supplier of resulting collection
* @param <I> the input type
* @param <O> the output type
* @param <C> the collecting type
* @return the collection given by the supplier
*/
public static <I, O, C extends Collection<O>> C collect(Iterable<? extends I> iterable, Predicate<I> acceptFilter, Function<I, O> mapper,
Supplier<C> target) {
return collect(iterable, acceptFilter, mapper, x -> true, target);
}
/**
* Applies a filter and a mapper over an {@link Iterable} to collect objects and puts them into a collection if it's accepted by a second
* predicate.
*
* @param iterable the source
* @param acceptFilter the accepting condition
* @param mapper the mapping function
* @param mappedValueFilter the accepting condition for the mapped value
* @param target the supplier of resulting collection
* @param <I> the input type
* @param <O> the output type
* @param <C> the collecting type
* @return the collection given by the supplier
*/
public static <I, O, C extends Collection<O>> C collect(Iterable<? extends I> iterable, Predicate<I> acceptFilter, Function<I, O> mapper,
Predicate<O> mappedValueFilter, Supplier<C> target) {
C result = target.get();
for (I pawn : iterable) {
if (acceptFilter.test(pawn)) {
O mappedPawn = mapper.apply(pawn);
if (mappedValueFilter.test(mappedPawn)) {
result.add(mappedPawn);
}
}
}
return result;
}
/**
* Copies an {@link Iterable} to a {@link List}
*
* @param iterable an {@link Iterable}, not null
* @return a new {@link List}<E> containing all elements of <t>iterable</t>
*/
public static <E> List<E> copy(Iterable<? extends E> iterable) {
return copy(iterable, new ArrayList<>());
}
/**
* Copies an {@link Iterator} to a {@link List}
*
* @param iterator an {@link Iterator}, not null
* @return a new {@link List}<E> containing all elements of <t>iterator</t>
*/
public static <E> List<E> copy(Iterator<? extends E> iterator) {
return copy(iterator, new ArrayList<>());
}
/**
* Copies an {@link Iterable} to a given {@link Collection}
*
* @param iterable an {@link Iterable}, not null
* @return the given {@link List}
*/
public static <E, C extends Collection<E>> C copy(Iterable<? extends E> iterable, C result) {
if (iterable instanceof Collection) {
result.addAll((Collection<E>) iterable);
} else {
copy(iterable.iterator(), result);
}
return result;
}
/**
* Copies an {@link Iterator} to a given {@link List}
*
* @param iterator an {@link Iterator}, not null
* @return the given {@link List}
*/
public static <E, C extends Collection<E>> C copy(Iterator<? extends E> iterator, C result) {
iterator.forEachRemaining(result::add);
return result;
}
/**
* Gives the intersection between two {@link Collection}s.
* Implementation has no particular optimization, it is based on a {@link HashSet}, hence comparison is based on
* {@link Object#equals(Object)}).
*
* @param c1 a {@link Collection}, not null
* @param c2 a {@link Collection}, not null
* @param <E> type of elements
* @return the intersection between two {@link Collection}s
*/
public static <E> Set<E> intersect(Collection<? extends E> c1, Collection<? extends E> c2) {
Set<E> copy = new HashSet<>(c1);
copy.retainAll(c2);
return copy;
}
/**
* Gives the intersection between two {@link Collection}s by comparing objects with a {@link Comparator}
* Implementation has no particular optimization, it is based on a {@link java.util.TreeSet}.
*
* @param c1 a {@link Collection}, not null
* @param c2 a {@link Collection}, not null
* @param <E> type of elements
* @return the intersection between two {@link Collection}s according to the given {@link Comparator}
*/
public static <E, T> Set<E> intersect(Collection<E> c1, Collection<T> c2, BiPredicate<E, T> predicate) {
Set<E> copy = new HashSet<>(c1);
copy.removeIf(e -> !contains(c2, c -> predicate.test(e, c)));
return copy;
}
/**
* Gives the complement of c2 in c1 : all elements of c1 that are not member of c2
* Implementation has no particular optimization, it is based on a {@link HashSet#removeAll(Collection)} and as such
* is based on {@link Object#equals}
*
* @param c1 a {@link Collection}, not null
* @param c2 a {@link Collection}, not null
* @param <E> type of elements
* @return the complement of c1 in c2
*/
public static <E> Set<E> minus(Collection<? extends E> c1, Collection<? extends E> c2) {
return minus(c1, c2, (Function<Collection<? extends E>, HashSet<E>>) HashSet::new);
}
/**
* Gives the complement of c2 in c1 : all elements of c1 that are not member of c2
* Implementation has no particular optimization, it is based on a {@link HashSet#removeAll(Collection)} and as such
* is based on {@link Object#equals}.
* Result is pushed to given resultHolder.
*
* @param c1 a {@link Collection}, not null
* @param c2 a {@link Collection}, not null
* @param resultHolder function expected to give resulting {@link Set}. Argument is c1.
* @param <E> type of elements
* @return the complement of c1 in c2
*/
public static <E, S extends Set<E>> S minus(Collection<? extends E> c1, Collection<? extends E> c2, Function<Collection<? extends E>, S> resultHolder) {
S copy = resultHolder.apply(c1);
copy.removeAll(c2);
return copy;
}
/**
* Gives the complement of c2 in c1 : all elements of c1 that are not member of c2 by comparing objects with a {@link Comparator}
* Implementation has no particular optimization, it is based on a {@link java.util.TreeSet}.
*
* @param c1 a {@link Collection}, not null
* @param c2 a {@link Collection}, not null
* @param <E> type of elements
* @return the complement of c1 in c2
*/
public static <E, T> Set<E> minus(Collection<? extends E> c1, Collection<? extends T> c2, BiPredicate<E, T> predicate) {
Set<E> copy = new HashSet<>(c1);
copy.removeIf(e -> contains(c2, c -> predicate.test(e, c)));
return copy;
}
public static <E> boolean equals(Iterable<E> it1, Iterable<E> it2, BiPredicate<E, E> predicate) {
if (it1 == it2)
return true;
Iterator<E> e1 = it1.iterator();
Iterator<E> e2 = it2.iterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
E o2 = e2.next();
if ((o1 == null ^ o2 == null) || !predicate.test(o1, o2)) {
return false;
}
}
return !(e1.hasNext() || e2.hasNext());
}
/**
* Puts 2 {@link Iterator}s side by side in a {@link Map}.
* If {@link Iterator}s are not of same length then null elements will be used into the {@link Map}.
* This leads to a cumulative null key if {@code values} {@link Iterator} is larger than {@code keys} {@link Iterator} because all overflowing
* elements will be put onto a null key.
*
* @param keys {@link Iterator} which elements will be used as keys
* @param values {@link Iterator} which elements will be used as values
* @param <K> type of keys
* @param <V> type of values
* @return a new {@link HashMap} composed of keys and values from both {@link Iterator}s
*/
public static <K, V> Map<K, V> pair(Iterable<K> keys, Iterable<V> values) {
return pair(keys, values, HashMap::new);
}
/**
* Puts 2 {@link Iterator}s side by side in a {@link Map}.
* If {@link Iterator}s are not of same length then null elements will be used into the {@link Map}.
* This leads to a cumulative null key if {@code values} {@link Iterator} is larger than {@code keys} {@link Iterator} because all overflowing
* elements will be put onto a null key.
*
* @param keys {@link Iterator} which elements will be used as keys
* @param values {@link Iterator} which elements will be used as values
* @param target a provider of a {@link Map}
* @param <K> type of keys
* @param <V> type of values
* @return a new {@link HashMap} composed of keys and values from both {@link Iterator}s
*/
public static <K, V, M extends Map<K, V>> M pair(Iterable<K> keys, Iterable<V> values, Supplier<M> target) {
UntilBothIterator<K, V> bothIterator = new UntilBothIterator<>(keys, values);
return map(() -> bothIterator, Duo::getLeft, Duo::getRight, target);
}
/**
* Creates an {@link Iterable} by applying a mapping function on each element of the {@link Iterator} given by
* the source {@link Iterable}.
*
* @param iterable the {@link Iterable} that gives the source of the {@link Iterator} on which the mapping function will be applied
* @param mapper the mapping function of each element
* @return an {@link Iterable} which {@link Iterator} is one backed by given {@link Iterable} {@link Iterator} combined with mapping function
* @param <E> {@link Iterable} {@link Iterator} source element type
* @param <O> {@link Iterable} {@link Iterator} result element type
* @see #mappingIterator(Iterator, Function)
*/
public static <E, O> Iterable<O> mappingIterator(Iterable<E> iterable, Function<E, O> mapper) {
return () -> mappingIterator(iterable.iterator(), mapper);
}
/**
* Creates an {@link Iterator} by applying a mapping function on each element of the given {@link Iterator}.
*
* @param iterator the {@link Iterator} on which the mapping function will be applied
* @param mapper the mapping function of each element
* @return an {@link Iterator} backed by given {@link Iterator} combined with mapping function
* @param <E> {@link Iterator} source element type
* @param <O> {@link Iterator} result element type
* @see #mappingIterator(Iterable, Function)
*/
public static <E, O> Iterator<O> mappingIterator(Iterator<E> iterator, Function<E, O> mapper) {
return new Iterator<O>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public O next() {
return mapper.apply(iterator.next());
}
};
}
/**
* Converts an {@link Iterator} to a {@link Stream}.
* If the {@link Iterator} comes from a {@link Collection}, then prefer usage of {@link Collection#stream()}
*
* @param iterator an {@link Iterator}, not null
* @return a {@link Stream} than will iterate over the {@link Iterator} passed as argument
*/
public static <E> Stream<E> stream(Iterator<E> iterator) {
return stream(() -> iterator);
}
/**
* Converts an {@link Iterable} to a {@link Stream}.
* If the {@link Iterable} is a {@link Collection}, then prefer usage of {@link Collection#stream()}
*
* @param iterable an {@link Iterable}, not null
* @return a {@link Stream} than will iterate over the {@link Iterable} passed as argument
*/
public static <E> Stream<E> stream(Iterable<? extends E> iterable) {
// StreamSupport knows how to convert an Iterable to a stream
return (Stream<E>) StreamSupport.stream(iterable.spliterator(), false);
}
/**
* Concatenates given {@link Iterable}s as a single one to have a linear view of all of them.
*
* @param iterables some {@link Iterable}s
* @return a {@link Iterable} than will iterate over all given {@link Iterable}s
*/
public static <E> Iterator<E> concat(Iterable<E>... iterables) {
return new IteratorIterator<>(iterables);
}
/**
* Concatenates given {@link Iterator}s as a single one to have a linear view of all of them.
*
* @param iterators some {@link Iterator}s
* @return a {@link Iterator} than will iterate over all given {@link Iterator}s
*/
public static <E> Iterator<E> concat(Iterator<E>... iterators) {
return new IteratorIterator<>(iterators);
}
/**
* Wraps given array of elements into an {@link Iterator}.
*
* @param elements elements to be iterated
* @return an {@link Iterator} of given elements
* @param <E> the elements type
*/
@SafeVarargs // method body doesn't handle improperly varargs parameter so it would generate ClassCastException
public static <E> Iterator<E> ofElements(E... elements ) {
return new ArrayIterator<>(elements);
}
/**
* Consumes an {@link Iterable} with an action that gets index of each element of the {@link Iterable}
*
* @param iterable any {@link Iterable}
* @param action an action that needs index and element as argument
* @param <E> {@link Iterable} elements type
*/
public static <E> void iterate(Iterable<E> iterable, BiConsumer<Integer, E> action) {
int i = 0;
for (E e : iterable) {
action.accept(i++, e);
}
}
/**
* Consumes an {@link Iterator} with an action that gets index of each element of the {@link Iterator}
*
* @param iterator any {@link Iterator}
* @param action an action that needs index and element as argument
* @param <E> {@link Iterator} elements type
*/
public static <E> void iterate(Iterator<E> iterator, BiConsumer<Integer, E> action) {
iterate(() -> iterator, action);
}
/**
* Keep elements of an {@link Iterable} that doesn't match a {@link Predicate}
*
* @param iterable any {@link Iterable}
* @param includer any {@link Predicate}
* @param <E> type of elements
* @return given iterable without elements that doesn't match the given predicate
*/
public static <E> Iterable<E> filter(Iterable<E> iterable, Predicate<E> includer) {
return () -> filter(iterable.iterator(), includer);
}
/**
* Keep elements of an {@link Iterator} that doesn't match a {@link Predicate}
*
* @param iterator any {@link Iterator}
* @param includer any {@link Predicate}
* @param <E> type of elements
* @return given iterator without elements that doesn't match the given predicate
*/
public static <E> Iterator<E> filter(Iterator<E> iterator, Predicate<E> includer) {
return new Iterator<E>() {
private boolean hasNext = true;
private E currentItem = null;
private final Iterator<E> delegate = iterator;
private void lookAhead() {
while (hasNext = delegate.hasNext()) {
E item = delegate.next();
if (!includer.test(item)) {
hasNext = false;
currentItem = null;
} else {
hasNext = true;
currentItem = item;
}
if (hasNext) {
break;
}
}
}
@Override
public boolean hasNext() {
lookAhead();
return hasNext;
}
@Override
public E next() {
if (!hasNext) {
// this is necessary to be compliant with Iterator#next(..) contract
throw new NoSuchElementException();
}
return currentItem;
}
@Override
public void remove() {
delegate.remove();
}
};
}
/**
* Finds the first predicate-matching element into an {@link Iterable}
*
* @param iterable the {@link Iterable} to scan
* @param predicate the test to execute for equality
* @param <I> input type
* @return null if no element matches the predicate
*/
public static <I> I find(Iterable<I> iterable, Predicate<I> predicate) {
return find(iterable.iterator(), predicate);
}
/**
* Finds the first predicate-matching element into an {@link Iterator}
*
* @param iterator the {@link Iterator} to scan
* @param predicate the test to execute for equality
* @param <I> input type
* @return null if no element matches the predicate
*/
@SuppressWarnings("squid:AssignmentInSubExpressionCheck" /* simple algorithm, doesn't require to extract the assignment from if */)
public static <I> I find(Iterator<I> iterator, Predicate<I> predicate) {
I result = null;
boolean found = false;
while (iterator.hasNext() && !found) {
I step = iterator.next();
if (found = predicate.test(step)) {
result = step;
}
}
return result;
}
/**
* Finds the first predicate-matching element (according to mapper) into the {@link Iterator} of an {@link Iterable}
*
* @param iterable the {@link Iterable} to scan
* @param mapper the mapper to extract the value to test
* @param predicate the test to execute for equality
* @param <I> input type
* @param <O> output type
* @return null if no mapped values matches the predicate
*/
public static <I, O> Duo<I, O> find(Iterable<I> iterable, Function<I, O> mapper, Predicate<O> predicate) {
return find(iterable.iterator(), mapper, predicate);
}
/**
* Finds the first predicate-matching element (according to mapper) into an {@link Iterator}
*
* @param iterator the {@link Iterator} to scan
* @param mapper the mapper to extract the value to test
* @param predicate the test to execute for equality
* @param <I> input type
* @param <O> output type
* @return null if no mapped values matches the predicate
*/
@SuppressWarnings("squid:AssignmentInSubExpressionCheck" /* simple algorithm, doesn't require to extract the assignment from if */)
public static <I, O> Duo<I, O> find(Iterator<I> iterator, Function<I, O> mapper, Predicate<O> predicate) {
Duo<I, O> result = null;
boolean found = false;
while (iterator.hasNext() && !found) {
I step = iterator.next();
O mapperResult = mapper.apply(step);
// voluntary variable reassignment
//noinspection ReassignedVariable
if (found = predicate.test(mapperResult)) {
result = new Duo<>(step, mapperResult);
}
}
return result;
}
/**
* Consumes all predicate-matching elements of an {@link Iterable}
*
* @param iterable the {@link Iterable} to scan
* @param matcher the test to execute for equality
* @param foundConsumer will be called with every matching element and its index
* @param <E> input type
*/
public static <E> void consume(Iterable<E> iterable, Predicate<E> matcher, BiConsumer<E, Integer> foundConsumer) {
consume(iterable.iterator(), matcher, foundConsumer);
}
/**
* Consumes all predicate-matching elements of an {@link Iterator}
*
* @param iterator the {@link Iterator} to scan
* @param matcher the test to execute for equality
* @param foundConsumer will be called with every matching element and its index
* @param <E> input type
*/
public static <E> void consume(Iterator<E> iterator, Predicate<E> matcher, BiConsumer<E, Integer> foundConsumer) {
int index = 0;
while (iterator.hasNext()) {
E step = iterator.next();
if (matcher.test(step)) {
foundConsumer.accept(step, index);
}
index++;
}
}
/**
* Equivalent of {@link #consume(Iterator, Predicate, BiConsumer)} with a {@link Stream} as input
*
* @param stream the {@link Stream} to scan
* @param matcher the test to execute for equality
* @param foundConsumer will be called with every matching element and its index
* @param <E> input type
*/
public static <E> void consume(Stream<E> stream, Predicate<E> matcher, BiConsumer<E, Integer> foundConsumer) {
final MutableInt index = new MutableInt(-1);
stream.map(e -> new Duo<>(e, index.increment()))
.filter(d -> matcher.test(d.getLeft()))
.forEach(d -> foundConsumer.accept(d.getLeft(), d.getRight()));
}
/**
* Indicates if an {@link Iterator} contains a predicate-matching element
*
* @param iterator the {@link Iterator} to scan
* @param <I> input type
* @return true if a value that matches the {@link Predicate} is found
*/
public static <I> boolean contains(Iterator<I> iterator, Predicate<I> predicate) {
return find(iterator, predicate) != null;
}
/**
* Indicates if an {@link Iterable} contains a predicate-matching element
*
* @param iterable the {@link Iterable} to scan
* @param <I> input type
* @return true if a value that matches the {@link Predicate} is found
*/
public static <I> boolean contains(Iterable<I> iterable, Predicate<I> predicate) {
return find(iterable.iterator(), predicate) != null;
}
/**
* Indicates if an {@link Iterator} contains a predicate-matching element after applying a mapper to the elements
*
* @param iterator the {@link Iterator} to scan
* @param mapper the mapper to extract the value to test
* @param <I> input type
* @param <O> output type
* @return true if a value that matches the {@link Predicate} is found
*/
public static <I, O> boolean contains(Iterator<I> iterator, Function<I, O> mapper, Predicate<O> predicate) {
return find(iterator, mapper, predicate) != null;
}
/**
* Indicates if an {@link Iterable} contains a predicate-matching element after applying a mapper to the elements
*
* @param iterable the {@link Iterable} to scan
* @param mapper the mapper to extract the value to test
* @param <I> input type
* @param <O> output type
* @return true if a value that matches the {@link Predicate} is found
*/
public static <I, O> boolean contains(Iterable<I> iterable, Function<I, O> mapper, Predicate<O> predicate) {
return find(iterable.iterator(), mapper, predicate) != null;
}
/**
* Returns the index of the first occurrence of the specified element in the given {@link Iterable},
* or -1 if this {@link Iterable} does not contain the element.
*
* @param iterable the {@link Iterable} to search
* @param o element to search for
* @return the index of the first occurrence of the specified element in the specified {@link Iterable}, or -1 if the element is not found
*/
public static <E> int indexOf(Iterable<E> iterable, E o) {
if (iterable instanceof List) {
return ((List<?>) iterable).indexOf(o);
}
int index = 0;
for (E element : iterable) {
if (Objects.equals(o, element)) {
return index;
}
index++;
}
return -1;
}
public static <E> Iterator<E> reverseIterator(List<E> list) {
return new ReverseListIterator<>(list);
}
/**
* Creates an {@link Iterator} in reverse order of the given {@link AbstractCollection}. Be aware that it requires to create a copy of the
* collection as an array, hence the collection can't be modified by the resulting iterator so it is {@link ReadOnlyIterator}.
*
* @param collection any (non null) {@link AbstractCollection}
* @param <E> collection elements type
* @return a reverse-order {@link Iterator} of the given collection
*/
public static <E> ReadOnlyIterator<E> reverseIterator(AbstractCollection<E> collection) {
return new ReverseArrayIterator<>((E[]) collection.toArray());
}
private Iterables() {}
/**
* Divides the iven {@link Iterable} into consecutive, equally-sized pieces (except possibly the last, which may be smaller)
*
* @param data an Iterable
* @param blockSize the size of blocks to be created (the last will contain remaining elements)
* @return a {@link List} of blocks of elements
*/
public static <E> List<List<E>> chunk(Iterable<E> data, int blockSize) {
final List<List<E>> blocks = new ArrayList<>();
// we ensure to have a list to allow sublist(..) usage afterward
List<E> dataAsList = asList(data);
int i = 0;
int dataSize = dataAsList.size();
int blockCount = dataSize / blockSize;
// if some remain, an additional block must be created
if (dataSize % blockSize != 0) {
blockCount++;
}
// parcelling
while (i < blockCount) {
blocks.add(new ArrayList<>(dataAsList.subList(i * blockSize, Math.min(dataSize, ++i * blockSize))));
}
return blocks;
}
}