/**
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.collect;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.Spliterator;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Collector;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import com.google.common.collect.ImmutableMap;
/**
* A stream implementation which adds methods for manipulating keys and values when streaming over map entries.
*
* @param <K> the key type
* @param <V> the value type
*/
public final class MapStream<K, V>
implements Stream<Map.Entry<K, V>> {
/** The stream of map entries. */
private final Stream<Map.Entry<K, V>> underlying;
/**
* Returns a stream over the entries in the map.
*
* @param <K> the key type
* @param <V> the value type
* @param map a map
* @return a stream over the entries in the map
*/
public static <K, V> MapStream<K, V> of(Map<K, V> map) {
return new MapStream<>(map.entrySet().stream());
}
/**
* Returns a stream of map entries where the values are taken from a collection and the keys are created by
* applying a function to each value.
*
* @param <K> the key type
* @param <V> the value type
* @param collection the collection of values
* @param keyFunction a function which returns the key for a value
* @return a stream of map entries derived from the values in the collection
*/
public static <K, V> MapStream<K, V> of(Collection<V> collection, Function<V, K> keyFunction) {
return new MapStream<>(collection.stream().map(v -> entry(keyFunction.apply(v), v)));
}
/**
* Returns a stream of map entries where the values are taken from a stream and the keys are created by
* applying a function to each value.
*
* @param <K> the key type
* @param <V> the value type
* @param stream the stream of values
* @param keyFunction a function which returns the key for a value
* @return a stream of map entries derived from the values in the stream
*/
public static <K, V> MapStream<K, V> of(Stream<V> stream, Function<V, K> keyFunction) {
return new MapStream<>(stream.map(v -> entry(keyFunction.apply(v), v)));
}
/**
* Returns an empty map stream.
*
* @param <K> the key type
* @param <V> the value type
* @return an empty map stream
*/
public static <K, V> MapStream<K, V> empty() {
return new MapStream<>(Stream.empty());
}
private MapStream(Stream<Map.Entry<K, V>> underlying) {
this.underlying = underlying;
}
//--------------------------------------------------------------------------------------------------
/**
* Filters the stream by applying the predicate function to each key and value.
* <p>
* Entries are included in the returned stream if the predicate function returns true.
*
* @param predicate a predicate function applied to each key and value in the stream
* @return a stream including the entries for which the predicate function returned true
*/
public MapStream<K, V> filter(BiFunction<? super K, ? super V, Boolean> predicate) {
return wrap(underlying.filter(e -> predicate.apply(e.getKey(), e.getValue())));
}
/**
* Filters the stream by applying the predicate function to each key.
* <p>
* Entries are included in the returned stream if the predicate function returns true.
*
* @param predicate a predicate function applied to each key in the stream
* @return a stream including the entries for which the predicate function returned true
*/
public MapStream<K, V> filterKeys(Predicate<? super K> predicate) {
return wrap(underlying.filter(e -> predicate.test(e.getKey())));
}
/**
* Filters the stream by applying the predicate function to each value.
* <p>
* Entries are included in the returned stream if the predicate function returns true.
*
* @param predicate a predicate function applied to each value in the stream
* @return a stream including the entries for which the predicate function returned true
*/
public MapStream<K, V> filterValues(Predicate<? super V> predicate) {
return wrap(underlying.filter(e -> predicate.test(e.getValue())));
}
/**
* Transforms the keys in the stream by applying a mapper function to each key.
* <p>
* The values are unchanged.
*
* @param mapper a mapper function whose return value is used as the new key
* @param <R> the type of the new keys
* @return a stream of entries with the keys transformed and the values unchanged
*/
public <R> MapStream<R, V> mapKeys(Function<? super K, ? extends R> mapper) {
return wrap(underlying.map(e -> entry(mapper.apply(e.getKey()), e.getValue())));
}
/**
* Transforms the keys in the stream by applying a mapper function to each key and value.
* <p>
* The values are unchanged.
*
* @param mapper a mapper function whose return value is used as the new key
* @param <R> the type of the new keys
* @return a stream of entries with the keys transformed and the values unchanged
*/
public <R> MapStream<R, V> mapKeys(BiFunction<? super K, ? super V, ? extends R> mapper) {
return wrap(underlying.map(e -> entry(mapper.apply(e.getKey(), e.getValue()), e.getValue())));
}
/**
* Transforms the values in the stream by applying a mapper function to each value.
* <p>
* The keys are unchanged.
*
* @param mapper a mapper function whose return value is used as the new value
* @param <R> the type of the new values
* @return a stream of entries with the values transformed and the keys unchanged
*/
public <R> MapStream<K, R> mapValues(Function<? super V, ? extends R> mapper) {
return wrap(underlying.map(e -> entry(e.getKey(), mapper.apply(e.getValue()))));
}
/**
* Transforms the values in the stream by applying a mapper function to each key and value.
* <p>
* The keys are unchanged.
*
* @param mapper a mapper function whose return value is used as the new value.
* @param <R> the type of the new values
* @return a stream of entries with the values transformed and the keys unchanged
*/
public <R> MapStream<K, R> mapValues(BiFunction<? super K, ? super V, ? extends R> mapper) {
return wrap(underlying.map(e -> entry(e.getKey(), mapper.apply(e.getKey(), e.getValue()))));
}
/**
* Transforms the entries in the stream by applying a mapper function to each key and value.
*
* @param mapper a mapper function whose return values are included in the new stream
* @param <R> the type of elements in the new stream
* @return a stream containing the values returned from the mapper function
*/
public <R> Stream<R> map(BiFunction<? super K, ? super V, ? extends R> mapper) {
return underlying.map(e -> mapper.apply(e.getKey(), e.getValue()));
}
/**
* Returns an immutable map built from the entries in the stream.
* <p>
* The keys must be unique or an exception will be thrown. Duplicate keys can be handled by using
* {@link #collect(Collector)} and {@code Collectors.toMap}.
*
* @return an immutable map built from the entries in the stream
*/
public ImmutableMap<K, V> toMap() {
return underlying.collect(Guavate.toImmutableMap(e -> e.getKey(), e -> e.getValue()));
}
/**
* Returns an immutable map built from the entries in the stream.
* <p>
* If the same key maps to multiple values the merge function is invoked with both values and the return
* value is used in the map.
*
* @param mergeFn function used to merge values when the same key appears multiple times in the stream
* @return an immutable map built from the entries in the stream
*/
public ImmutableMap<K, V> toMap(BiFunction<? super V, ? super V, ? extends V> mergeFn) {
return underlying.collect(Guavate.toImmutableMap(e -> e.getKey(), e -> e.getValue(), mergeFn));
}
/**
* Performs an action for each entry in the stream, passing the key and value to the action.
*
* @param action an action performed for each entry in the stream
*/
public void forEach(BiConsumer<? super K, ? super V> action) {
underlying.forEach(e -> action.accept(e.getKey(), e.getValue()));
}
//--------------------------------------------------------------------------------------------------
@Override
public MapStream<K, V> filter(Predicate<? super Map.Entry<K, V>> predicate) {
return wrap(underlying.filter(predicate));
}
@Override
public <R> Stream<R> map(Function<? super Map.Entry<K, V>, ? extends R> mapper) {
return underlying.map(mapper);
}
@Override
public IntStream mapToInt(ToIntFunction<? super Map.Entry<K, V>> mapper) {
return underlying.mapToInt(mapper);
}
@Override
public LongStream mapToLong(ToLongFunction<? super Map.Entry<K, V>> mapper) {
return underlying.mapToLong(mapper);
}
@Override
public DoubleStream mapToDouble(ToDoubleFunction<? super Map.Entry<K, V>> mapper) {
return underlying.mapToDouble(mapper);
}
@Override
public <R> Stream<R> flatMap(Function<? super Map.Entry<K, V>, ? extends Stream<? extends R>> mapper) {
return underlying.flatMap(mapper);
}
@Override
public IntStream flatMapToInt(Function<? super Map.Entry<K, V>, ? extends IntStream> mapper) {
return underlying.flatMapToInt(mapper);
}
@Override
public LongStream flatMapToLong(Function<? super Map.Entry<K, V>, ? extends LongStream> mapper) {
return underlying.flatMapToLong(mapper);
}
@Override
public DoubleStream flatMapToDouble(Function<? super Map.Entry<K, V>, ? extends DoubleStream> mapper) {
return underlying.flatMapToDouble(mapper);
}
@Override
public MapStream<K, V> distinct() {
return wrap(underlying.distinct());
}
@Override
public MapStream<K, V> sorted() {
return wrap(underlying.sorted());
}
@Override
public MapStream<K, V> sorted(Comparator<? super Map.Entry<K, V>> comparator) {
return wrap(underlying.sorted(comparator));
}
@Override
public MapStream<K, V> peek(Consumer<? super Map.Entry<K, V>> action) {
return wrap(underlying.peek(action));
}
@Override
public MapStream<K, V> limit(long maxSize) {
return wrap(underlying.limit(maxSize));
}
@Override
public MapStream<K, V> skip(long n) {
return wrap(underlying.skip(n));
}
@Override
public void forEach(Consumer<? super Map.Entry<K, V>> action) {
underlying.forEach(action);
}
@Override
public void forEachOrdered(Consumer<? super Map.Entry<K, V>> action) {
underlying.forEachOrdered(action);
}
@Override
public Object[] toArray() {
return underlying.toArray();
}
@Override
public <A> A[] toArray(IntFunction<A[]> generator) {
return underlying.toArray(generator);
}
@Override
public Map.Entry<K, V> reduce(Map.Entry<K, V> identity, BinaryOperator<Map.Entry<K, V>> accumulator) {
return underlying.reduce(identity, accumulator);
}
@Override
public Optional<Map.Entry<K, V>> reduce(BinaryOperator<Map.Entry<K, V>> accumulator) {
return underlying.reduce(accumulator);
}
@Override
public <U> U reduce(U identity, BiFunction<U, ? super Map.Entry<K, V>, U> accumulator, BinaryOperator<U> combiner) {
return underlying.reduce(identity, accumulator, combiner);
}
@Override
public <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super Map.Entry<K, V>> accumulator, BiConsumer<R, R> combiner) {
return underlying.collect(supplier, accumulator, combiner);
}
@Override
public <R, A> R collect(Collector<? super Map.Entry<K, V>, A, R> collector) {
return underlying.collect(collector);
}
@Override
public Optional<Map.Entry<K, V>> min(Comparator<? super Map.Entry<K, V>> comparator) {
return underlying.min(comparator);
}
@Override
public Optional<Map.Entry<K, V>> max(Comparator<? super Map.Entry<K, V>> comparator) {
return underlying.max(comparator);
}
@Override
public long count() {
return underlying.count();
}
@Override
public boolean anyMatch(Predicate<? super Map.Entry<K, V>> predicate) {
return underlying.anyMatch(predicate);
}
@Override
public boolean allMatch(Predicate<? super Map.Entry<K, V>> predicate) {
return underlying.allMatch(predicate);
}
@Override
public boolean noneMatch(Predicate<? super Map.Entry<K, V>> predicate) {
return underlying.noneMatch(predicate);
}
@Override
public Optional<Map.Entry<K, V>> findFirst() {
return underlying.findFirst();
}
@Override
public Optional<Map.Entry<K, V>> findAny() {
return underlying.findAny();
}
@Override
public Iterator<Map.Entry<K, V>> iterator() {
return underlying.iterator();
}
@Override
public Spliterator<Map.Entry<K, V>> spliterator() {
return underlying.spliterator();
}
@Override
public boolean isParallel() {
return underlying.isParallel();
}
@Override
public MapStream<K, V> sequential() {
return wrap(underlying.sequential());
}
@Override
public MapStream<K, V> parallel() {
return wrap(underlying.parallel());
}
@Override
public MapStream<K, V> unordered() {
return wrap(underlying.unordered());
}
@Override
public MapStream<K, V> onClose(Runnable closeHandler) {
return wrap(underlying.onClose(closeHandler));
}
@Override
public void close() {
underlying.close();
}
//--------------------------------------------------------------------------------------------------
private static <K, V> Map.Entry<K, V> entry(K k, V v) {
return new AbstractMap.SimpleImmutableEntry<>(k, v);
}
private static <K, V> MapStream<K, V> wrap(Stream<Map.Entry<K, V>> underlying) {
return new MapStream<>(underlying);
}
}