org.openstreetmap.atlas.utilities.collections.Iterables Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of atlas Show documentation
Show all versions of atlas Show documentation
"Library to load OSM data into an Atlas format"
The newest version!
package org.openstreetmap.atlas.utilities.collections;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.function.LongFunction;
import java.util.function.Predicate;
import java.util.function.ToLongFunction;
import java.util.stream.StreamSupport;
/**
* Iterable utility methods
*
* @author matthieun
*/
public final class Iterables
{
/**
* Adds the contents of the from Iterable to the addHere collection, and returns true if items
* were added. It's possible that nothing has changed if addHere is a set
*
* @param addHere
* where to add the items
* @param from
* where to get the items
* @param
* what kind of objects we're copying
* @return true if addHere has changed, false otherwise
*/
public static boolean addAll(final Collection addHere, final Iterable from)
{
final int oldSize = addHere.size();
StreamSupport.stream(from.spliterator(), false).forEach(addHere::add);
return oldSize < addHere.size();
}
/**
* Strip down any {@link Iterable} into .. just an {@link Iterable}.
*
* @param types
* The {@link Iterable} to strip down
* @param
* The type of the {@link Iterable}
* @return The translated {@link Iterable}
*/
public static Iterable asIterable(final Iterable types)
{
return types::iterator;
}
/**
* Translate an {@link Iterable} into a {@link List}
*
* @param types
* The {@link Iterable} to translate
* @param
* The type of the {@link Iterable}
* @return The translated {@link Iterable}
*/
public static List asList(final Iterable types)
{
if (types instanceof List)
{
return (List) types;
}
final int initialSize;
if (types instanceof Collection)
{
initialSize = ((Collection) types).size();
}
else
{
initialSize = 0;
}
final List result = new ArrayList<>(initialSize);
types.forEach(result::add);
return result;
}
/**
* Translate an array to an {@link List}. Unlike {@link java.util.Arrays#asList}, this method
* does not use the given array as the backing array.
*
* @param types
* The {@link Iterable} to translate
* @param
* The type of the {@link Iterable}
* @return The translated {@link Iterable}
*/
public static List asList(final T[] types)
{
// This avoids several grow calls, and ensures that we aren't using the backing array.
return new ArrayList<>(Arrays.asList(types));
}
/**
* Translate an iterable list of Map entries to a map
*
* @param types
* The {@link Iterable} to translate
* @param
* The type of key of the entry
* @param
* The type of value of the entry
* @return The translated {@link Iterable}
*/
public static Map asMap(final Iterable> types)
{
final Map result = new HashMap<>();
types.forEach(entry -> result.put(entry.getKey(), entry.getValue()));
return result;
}
/**
* Translate an {@link Iterable} into a {@link Queue}
*
* @param types
* The {@link Iterable} to translate
* @param
* The type of the {@link Iterable}
* @return The translated {@link Iterable}
*/
public static Queue asQueue(final Iterable types)
{
final Queue result = new LinkedList<>();
types.forEach(result::add);
return result;
}
/**
* Translate an {@link Iterable} into a {@link Set}
*
* @param types
* The {@link Iterable} to translate
* @param
* The type of the {@link Iterable}
* @return The translated {@link Iterable}
*/
public static Set asSet(final Iterable types)
{
final Set result = new HashSet<>();
types.forEach(result::add);
return result;
}
/**
* Translate an array to an {@link Set}
*
* @param types
* The {@link Iterable} to translate
* @param
* The type of the {@link Iterable}
* @return The translated {@link Iterable}
*/
public static Set asSet(final T[] types)
{
final Set result = new HashSet<>();
for (final T type : types)
{
result.add(type);
}
return result;
}
/**
* Translate an {@link Iterable} of items into a {@link SortedSet}
*
* @param types
* The {@link Iterable} to translate
* @param
* The type of the {@link Iterable}
* @return The translated {@link Iterable}
*/
public static SortedSet asSortedSet(final Iterable types)
{
final SortedSet result = new TreeSet<>();
types.forEach(result::add);
return result;
}
/**
* Test if an {@link Iterable} iterates at some point on an item.
*
* @param types
* The {@link Iterable} to test
* @param type
* The item to test
* @param
* The type of the {@link Iterable}
* @return True if the {@link Iterable} iterates at some point on the item.
*/
public static boolean contains(final Iterable types, final T type)
{
if (types instanceof Collection)
{
return ((Collection) types).contains(type);
}
for (final T candidate : types)
{
if (candidate.equals(type))
{
return true;
}
}
return false;
}
/**
* Count a set of values
*
* @param types
* The {@link Iterable} of input type
* @param typeCounter
* The function from type to count
* @param
* The type of the {@link Iterable}
* @return The total count
*/
public static long count(final Iterable types, final ToLongFunction typeCounter)
{
long result = 0;
for (final T type : types)
{
result += typeCounter.applyAsLong(type);
}
return result;
}
/**
* @param example
* A random object to specify the type
* @param
* The type of the {@link Iterable}
* @return An empty {@link Iterable} of the right type
*/
public static Iterable emptyIterable(final T example) // NOSONAR
{
return () -> new Iterator()
{
@Override
public boolean hasNext()
{
return false;
}
@Override
public T next()
{
throw new NoSuchElementException();
}
};
}
/**
* Test if two {@link Iterable}s iterate on the same items.
*
* @param that
* The first {@link Iterable}
* @param other
* The second iterable
* @param
* The type of the {@link Iterable}
* @return True if the two {@link Iterable}s iterate on the same items.
*/
public static boolean equals(final Iterable that, final Iterable other)
{
// Handle null iterables
// If they are both null, then equal
// If only one of them is null, then NOT equal
final boolean thatIsNull = that == null;
final boolean otherIsNull = other == null;
if (thatIsNull || otherIsNull)
{
return thatIsNull && otherIsNull;
}
// Iterables are not null, let's check for size first
// Then the values
final long thatSize = Iterables.size(that);
if (thatSize != Iterables.size(other))
{
return false;
}
final Iterator thatIterator = that.iterator();
final Iterator otherIterator = other.iterator();
while (thatIterator.hasNext())
{
if (!thatIterator.next().equals(otherIterator.next()))
{
return false;
}
}
return true;
}
/**
* Filter an {@link Iterable}
*
* @param input
* The {@link Iterable} to filter
* @param matcher
* The {@link Predicate} used to filter
* @param
* The type of the {@link Iterable}
* @return The filtered {@link Iterable}
*/
public static Iterable filter(final Iterable input, final Predicate matcher)
{
return filterTranslate(input, item -> item, matcher);
}
/**
* Translate an {@link Iterable} of items into a {@link FilteredIterable}
*
* @param types
* The {@link Iterable} to translate
* @param filterSet
* A set of identifiers for elements to skip (can be empty or have members)
* @param identifier
* A function that takes an element of T for the {@link Iterable} and returns the
* identifier for that element
* @param
* The type of the {@link Iterable}
* @param
* The type of the Identifier object for the elements in the {@link Iterable}
* @return The translated {@link Iterable}
*/
public static FilteredIterable filter(final Iterable types,
final Set filterSet, final Function identifier)
{
return new FilteredIterable<>(types, filterSet, identifier);
}
/**
* Translate an {@link Iterable} of type I to an {@link Iterable} of type O.
*
* @param input
* The input {@link Iterable}
* @param converter
* The converter from I to O
* @param matcher
* A {@link Predicate} on I that filters only the items to match
* @param
* The type of the input {@link Iterable}
* @param
* The type of the output {@link Iterable}
* @return The {@link Iterable} of O
*/
public static Iterable filterTranslate(final Iterable input,
final Function converter, final Predicate matcher)
{
return new Iterable()
{
@Override
public Iterator iterator()
{
return new Iterator()
{
private boolean consumed = true;
private final Iterator iterator = input.iterator();
private I next = null;
private boolean valid = false;
@Override
public boolean hasNext()
{
if (this.consumed)
{
this.next = null;
this.valid = false;
while (this.iterator.hasNext() && !this.valid)
{
this.next = this.iterator.next();
this.valid = matcher.test(this.next);
}
this.consumed = false;
}
return this.valid;
}
@Override
public O next()
{
if (hasNext())
{
this.consumed = true;
return converter.apply(this.next);
}
throw new NoSuchElementException();
}
};
}
@SuppressWarnings("unused")
public void useless()
{
// Unused
}
};
}
/**
* Get the first element of an {@link Iterable}
*
* @param types
* The items
* @param
* The type of the {@link Iterable}
* @return The first element in the {@link Iterable}, or empty if none.
*/
public static Optional first(final Iterable types)
{
return nth(types, 0);
}
public static Optional firstMatching(final Iterable types, final Predicate matcher)
{
return first(filter(types, matcher));
}
/**
* Create an {@link Iterable} from an {@link Enumeration}
*
* @param types
* The {@link Enumeration}
* @param
* The type of the {@link Iterable}
* @return The translated {@link Iterable}
*/
public static Iterable from(final Enumeration types)
{
return () -> new Iterator()
{
@Override
public boolean hasNext()
{
return types.hasMoreElements();
}
@Override
public T next()
{
if (!hasNext())
{
throw new NoSuchElementException();
}
return types.nextElement();
}
};
}
/**
* Create an {@link Iterable} from 0 to many items of the provided type
*
* @param types
* The 0 to many array of items to include
* @param
* The type of the {@link Iterable}
* @return The translated {@link Iterable}
*/
@SafeVarargs
public static Iterable from(final T... types)
{
return asList(types);
}
/**
* Get the head (first) element of an {@link Iterable}
*
* @param types
* The items
* @param
* The type of the {@link Iterable}
* @return The head element in the {@link Iterable}, or null if none.
*/
public static T head(final Iterable types)
{
final Iterator iterator = types.iterator();
return iterator.hasNext() ? iterator.next() : null;
}
/**
* Get an {@link Iterable} based on something that can return the value at a specific index.
*
* @param size
* The total size of the collection
* @param supplier
* The provider of the value based on the index
* @param
* The type to return within the {@link Iterable}
* @return The index based {@link Iterable}
*/
public static Iterable indexBasedIterable(final long size,
final LongFunction supplier)
{
return () -> new Iterator()
{
private long index = 0L;
@Override
public boolean hasNext()
{
return this.index < size;
}
@Override
public T next()
{
if (!hasNext())
{
throw new NoSuchElementException();
}
return supplier.apply(this.index++);
}
};
}
/**
* Determines if the given iterable is empty
*
* @param types
* The iterable to check
* @return {@code true} if the iterable contains no elements
*/
public static boolean isEmpty(final Iterable> types)
{
if (types instanceof Collection)
{
return ((Collection>) types).isEmpty();
}
return !types.iterator().hasNext();
}
/**
* Translate a passed array of Items to an {@link Iterable} of Items
*
* @param types
* The items
* @param
* The type of the {@link Iterable}
* @return An {@link Iterable} of items.
*/
public static Iterable iterable(@SuppressWarnings("unchecked") final T... types)
{
return indexBasedIterable(types.length, index -> types[(int) index]);
}
/**
* Build an new Iterable by prepending the head element to the tail iterable.
*
* @param head
* The item to place in the head position
* @param tail
* The items positioned after the head
* @param
* The type of the head and tail {@link Iterable}
* @return An {@link Iterable}
*/
public static Iterable join(final T head, final Iterable tail)
{
return () -> new Iterator()
{
private final Iterator tailIterator = tail.iterator();
private boolean headConsumed = false;
@Override
public boolean hasNext()
{
return !this.headConsumed || this.tailIterator.hasNext();
}
@Override
public T next()
{
if (this.headConsumed)
{
return this.tailIterator.next();
}
this.headConsumed = true;
return head;
}
};
}
/**
* Get the last element of an {@link Iterable}
*
* @param types
* The items
* @param
* The type of the {@link Iterable}
* @return The last element in the {@link Iterable}
*/
public static Optional last(final Iterable types)
{
T result = null;
if (types instanceof List)
{
final List list = (List) types;
if (!list.isEmpty())
{
result = list.get(list.size() - 1);
}
}
else
{
for (final T type : types)
{
result = type;
}
}
return Optional.ofNullable(result);
}
public static Optional lastMatching(final Iterable types, final Predicate matcher)
{
return last(filter(types, matcher));
}
/**
* Get the nth element of an {@link Iterable}
*
* @param types
* The items
* @param index
* The index at which to pick
* @param
* The type of the {@link Iterable}
* @return The first element in the {@link Iterable}, or empty if the iterable has no element at
* this index.
*/
public static Optional nth(final Iterable types, final long index)
{
long counter = 0L;
final Iterator iterator = types.iterator();
T result = iterator.hasNext() ? iterator.next() : null;
while (counter++ < index)
{
if (iterator.hasNext())
{
result = iterator.next();
}
else
{
result = null;
break;
}
}
return Optional.ofNullable(result);
}
/**
* Create a {@link StreamIterable} that uses parallelization
*
* @param source
* The {@link Iterable} to use as source
* @param
* The type of the source {@link Iterable}
* @return The corresponding {@link StreamIterable}
*/
public static StreamIterable parallelStream(final Iterable source)
{
return new StreamIterable<>(source, true);
}
public static void print(final Iterable input, final String name)
{
System.out.println(toString(input, name)); // NOSONAR
}
/**
* Iterate over an {@link Iterable} to get its size. If the {@link Iterable} is a sub instance
* of {@link Collection}, then it reads the size from it directly; it will not iterate
* unnecessarily.
*
* @param
* The type of the {@link Iterable}
* @param types
* The input {@link Iterable}
* @return The size of the {@link Iterable}
*/
public static long size(final Iterable types)
{
if (types instanceof Collection)
{
return ((Collection) types).size();
}
return count(types, type -> 1L);
}
/**
* Create a {@link StreamIterable}
*
* @param source
* The {@link Iterable} to use as source
* @param
* The type of the source {@link Iterable}
* @return The corresponding {@link StreamIterable}
*/
public static StreamIterable stream(final Iterable source)
{
return new StreamIterable<>(source);
}
/**
* Get an {@link Iterable} of all elements beyond the head.
*
* @param types
* The items
* @param
* The type of the {@link Iterable}
* @return An {@link Iterable}
*/
public static Iterable tail(final Iterable types)
{
final Iterator iterator = types.iterator();
if (iterator.hasNext())
{
iterator.next();
}
return () -> iterator;
}
/**
* Translate an array to an {@link List}
*
* @param types
* The {@link Iterable} to translate
* @param
* The type of the {@link Iterable}
* @return The translated {@link Iterable}
*/
@SafeVarargs
public static List toList(final T... types)
{
return asList(types);
}
/**
* Translate an {@link Iterable} to a {@link String}
*
* @param input
* The input {@link Iterable}
* @param
* The type of the {@link Iterable}
* @param name
* The name of the input {@link Iterable}
* @return A {@link String} representation of the {@link Iterable}
*/
public static String toString(final Iterable input, final String name)
{
return toString(input, name, ", ");
}
/**
* Translate an {@link Iterable} to a {@link String}
*
* @param input
* The input {@link Iterable}
* @param
* The type of the {@link Iterable}
* @param name
* The name of the input {@link Iterable}
* @param separator
* The separator to use between each item in the input {@link Iterable}
* @return A {@link String} representation of the {@link Iterable}
*/
public static String toString(final Iterable input, final String name,
final String separator)
{
final StringBuilder builder = new StringBuilder();
builder.append("[");
builder.append(name);
builder.append(": ");
long index = 0;
for (final T type : input)
{
if (index > 0)
{
builder.append(separator);
}
builder.append(type.toString());
index++;
}
builder.append("]");
return builder.toString();
}
/**
* Translate an {@link Iterable} of type I to an {@link Iterable} of type O.
*
* @param input
* The input {@link Iterable}
* @param converter
* The converter from I to O
* @param
* The type of the input {@link Iterable}
* @param
* The type of the output {@link Iterable}
* @return The {@link Iterable} of O
*/
public static Iterable translate(final Iterable input,
final Function converter)
{
return filterTranslate(input, converter, item -> true);
}
/**
* Translate an {@link Iterable} of type I to an {@link Iterable} of type O.
*
* @param input
* The input {@link Iterable}
* @param converter
* The converter from I to O
* @param
* The type of the input {@link Iterable}
* @param
* The type of the output {@link Iterable}
* @param matcher
* A {@link Predicate} on O that filters only the items to match
* @return The {@link Iterable} of O
*/
public static Iterable translateFilter(final Iterable input,
final Function converter, final Predicate matcher)
{
return Iterables.filter(Iterables.translate(input, converter), matcher);
}
/**
* Translate an {@link Iterable} of type I to an {@link Iterable} of O where each converter
* yields multiple O for each I.
*
* @param iterableIn
* The input {@link Iterable}
* @param converter
* The converter from I to multiple O
* @param
* The type of the input {@link Iterable}
* @param
* The type of the output {@link Iterable}
* @return The {@link Iterable} of O
*/
public static Iterable translateMulti(final Iterable iterableIn,
final Function> converter)
{
return new MultiIterable<>(Iterables.translate(iterableIn, converter));
}
/**
* Truncate an {@link Iterable}.
*
* @param types
* The {@link Iterable} to translate
* @param startIndex
* The index before which to truncate from the start
* @param indexFromEnd
* The index after which to truncate from the end
* @param
* The type of the {@link Iterable}
* @return The truncated {@link Iterable}
*/
public static Iterable truncate(final Iterable types, final int startIndex,
final int indexFromEnd)
{
return new SubIterable<>(types, startIndex, indexFromEnd);
}
private Iterables()
{
}
}