/* * Copyright 2015, 2016 Tagir Valeev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package one.util.streamex; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Spliterator; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.SortedMap; import java.util.Spliterators; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Collector.Characteristics; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; import static one.util.streamex.StreamExInternals.*; /** * A {@link Stream} of {@link Entry} objects which provides additional specific * functionality. * * <p> * While {@code EntryStream} implements {@code Iterable}, it is not a * general-purpose {@code Iterable} as it supports only a single * {@code Iterator}; invoking the {@link #iterator iterator} method to obtain a * second or subsequent iterator throws {@code IllegalStateException}. * * @author Tagir Valeev * * @param <K> the type of {@code Entry} keys * @param <V> the type of {@code Entry} values */ public class EntryStream<K, V> extends AbstractStreamEx<Entry<K, V>, EntryStream<K, V>> { EntryStream(Stream<? extends Entry<K, V>> stream, StreamContext context) { super(stream, context); } EntryStream(Spliterator<? extends Entry<K, V>> spliterator, StreamContext context) { super(spliterator, context); } @Override EntryStream<K, V> supply(Stream<Map.Entry<K, V>> stream) { return new EntryStream<>(stream, context); } @Override EntryStream<K, V> supply(Spliterator<Entry<K, V>> spliterator) { return new EntryStream<>(spliterator, context); } static <K, V> Consumer<? super Entry<K, V>> toConsumer(BiConsumer<? super K, ? super V> action) { return entry -> action.accept(entry.getKey(), entry.getValue()); } <M extends Map<K, V>> Consumer<? super Entry<K, V>> toMapConsumer(M map) { return entry -> addToMap(map, entry.getKey(), Objects.requireNonNull(entry.getValue())); } BiPredicate<? super Entry<K, V>, ? super Entry<K, V>> equalKeys() { return (e1, e2) -> Objects.equals(e1.getKey(), e2.getKey()); } static <K, V> Stream<Entry<K, V>> withValue(Stream<? extends K> s, V value) { return s == null ? null : s.map(key -> new SimpleImmutableEntry<>(key, value)); } static <K, V> Stream<Entry<K, V>> withKey(K key, Stream<? extends V> s) { return s == null ? null : s.map(value -> new SimpleImmutableEntry<>(key, value)); } static <K, V, R> Function<? super Entry<K, V>, ? extends R> toFunction( BiFunction<? super K, ? super V, ? extends R> mapper) { return entry -> mapper.apply(entry.getKey(), entry.getValue()); } /** * Returns a {@link StreamEx} of strings which are created joining the keys * and values of the current stream using the specified delimiter. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param delimiter the delimiter to be used between key and value * @return the new stream * @since 0.2.2 */ public StreamEx<String> join(CharSequence delimiter) { return map(entry -> String.valueOf(entry.getKey()) + delimiter + entry.getValue()); } /** * Returns a {@link StreamEx} of strings which are created joining the keys * and values of the current stream using the specified delimiter, with the * specified prefix and suffix. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param delimiter the delimiter to be used between key and value * @param prefix the sequence of characters to be used at the beginning of * each resulting string * @param suffix the sequence of characters to be used at the end of each * resulting string * @return the new stream * @since 0.2.2 */ public StreamEx<String> join(CharSequence delimiter, CharSequence prefix, CharSequence suffix) { return map(entry -> String.valueOf(prefix) + entry.getKey() + delimiter + entry.getValue() + suffix); } /** * Returns an {@code EntryStream} consisting of the entries whose keys are * results of replacing source keys with the contents of a mapped stream * produced by applying the provided mapping function to each source key and * values are left intact. Each mapped stream is * {@link java.util.stream.BaseStream#close() closed} after its contents * have been placed into this stream. (If a mapped stream is {@code null} an * empty stream is used, instead.) * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. * * @param <KK> The type of new keys * @param mapper a <a * href="package-summary.html#NonInterference">non-interfering </a>, * <a href="package-summary.html#Statelessness">stateless</a> * function to apply to each key which produces a stream of new keys * @return the new stream */ public <KK> EntryStream<KK, V> flatMapKeys(Function<? super K, ? extends Stream<? extends KK>> mapper) { return new EntryStream<>(stream().flatMap(e -> withValue(mapper.apply(e.getKey()), e.getValue())), context); } /** * Returns an {@code EntryStream} consisting of the entries whose keys are * results of replacing source keys with the contents of a mapped stream * produced by applying the provided mapping function and values are left * intact. Each mapped stream is {@link java.util.stream.BaseStream#close() * closed} after its contents have been placed into this stream. (If a * mapped stream is {@code null} an empty stream is used, instead.) * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. * * @param <KK> The type of new keys * @param mapper a <a * href="package-summary.html#NonInterference">non-interfering </a>, * <a href="package-summary.html#Statelessness">stateless</a> * function to apply to each key and value which produces a stream of * new keys * @return the new stream * @since 0.5.2 */ public <KK> EntryStream<KK, V> flatMapToKey(BiFunction<? super K, ? super V, ? extends Stream<? extends KK>> mapper) { return new EntryStream<>( stream().flatMap(e -> withValue(mapper.apply(e.getKey(), e.getValue()), e.getValue())), context); } /** * Returns an {@code EntryStream} consisting of the entries whose values are * results of replacing source values with the contents of a mapped stream * produced by applying the provided mapping function to each source value * and keys are left intact. Each mapped stream is * {@link java.util.stream.BaseStream#close() closed} after its contents * have been placed into this stream. (If a mapped stream is {@code null} an * empty stream is used, instead.) * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. * * @param <VV> The type of new values * @param mapper a <a * href="package-summary.html#NonInterference">non-interfering </a>, * <a href="package-summary.html#Statelessness">stateless</a> * function to apply to each value which produces a stream of new * values * @return the new stream */ public <VV> EntryStream<K, VV> flatMapValues(Function<? super V, ? extends Stream<? extends VV>> mapper) { return new EntryStream<>(stream().flatMap(e -> withKey(e.getKey(), mapper.apply(e.getValue()))), context); } /** * Returns an {@code EntryStream} consisting of the entries whose values are * results of replacing source values with the contents of a mapped stream * produced by applying the provided mapping function and keys are left * intact. Each mapped stream is {@link java.util.stream.BaseStream#close() * closed} after its contents have been placed into this stream. (If a * mapped stream is {@code null} an empty stream is used, instead.) * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. * * @param <VV> The type of new values * @param mapper a <a * href="package-summary.html#NonInterference">non-interfering </a>, * <a href="package-summary.html#Statelessness">stateless</a> * function to apply to each key and value which produces a stream of * new values * @return the new stream * @since 0.5.2 */ public <VV> EntryStream<K, VV> flatMapToValue( BiFunction<? super K, ? super V, ? extends Stream<? extends VV>> mapper) { return new EntryStream<>(stream().flatMap(e -> withKey(e.getKey(), mapper.apply(e.getKey(), e.getValue()))), context); } /** * Returns a stream consisting of the results of replacing each element of * this stream with the contents of a mapped stream produced by applying the * provided mapping function to each key-value pair. Each mapped stream is * closed after its contents have been placed into this stream. (If a mapped * stream is {@code null} an empty stream is used, instead.) * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param <R> The element type of the new stream * @param mapper a non-interfering, stateless function to apply to each * key-value pair which produces a stream of new values * @return the new stream * @since 0.3.0 */ public <R> StreamEx<R> flatMapKeyValue(BiFunction<? super K, ? super V, ? extends Stream<? extends R>> mapper) { return this.<R> flatMap(toFunction(mapper)); } /** * Returns a new {@code EntryStream} which is a concatenation of this stream * and the stream created from the supplied map entries. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate * operation</a>. * * <p> * May return this if the supplied map is empty and non-concurrent. * * @param map the map to prepend to the stream * @return the new stream * @since 0.2.1 */ public EntryStream<K, V> append(Map<K, V> map) { return appendSpliterator(null, map.entrySet().spliterator()); } /** * Returns a new {@code EntryStream} which is a concatenation of this stream * and the supplied key-value pair. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate * operation</a>. * * @param key the key of the new {@code Entry} to append to this stream * @param value the value of the new {@code Entry} to append to this stream * @return the new stream */ public EntryStream<K, V> append(K key, V value) { return appendSpliterator(null, new ConstSpliterator.OfRef<>(new SimpleImmutableEntry<>(key, value), 1, true)); } /** * Returns a new {@code EntryStream} which is a concatenation of this stream * and two supplied key-value pairs. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate * operation</a>. * * @param k1 the key of the first {@code Entry} to append to this stream * @param v1 the value of the first {@code Entry} to append to this stream * @param k2 the key of the second {@code Entry} to append to this stream * @param v2 the value of the second {@code Entry} to append to this stream * @return the new stream * @since 0.2.3 */ public EntryStream<K, V> append(K k1, V v1, K k2, V v2) { @SuppressWarnings("unchecked") SimpleImmutableEntry<K, V>[] array = new SimpleImmutableEntry[] { new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2) }; return appendSpliterator(null, Spliterators.spliterator(array, Spliterator.ORDERED)); } /** * Returns a new {@code EntryStream} which is a concatenation of this stream * and three supplied key-value pairs. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate * operation</a>. * * @param k1 the key of the first {@code Entry} to append to this stream * @param v1 the value of the first {@code Entry} to append to this stream * @param k2 the key of the second {@code Entry} to append to this stream * @param v2 the value of the second {@code Entry} to append to this stream * @param k3 the key of the third {@code Entry} to append to this stream * @param v3 the value of the third {@code Entry} to append to this stream * @return the new stream * @since 0.2.3 */ public EntryStream<K, V> append(K k1, V v1, K k2, V v2, K k3, V v3) { @SuppressWarnings("unchecked") SimpleImmutableEntry<K, V>[] array = new SimpleImmutableEntry[] { new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2), new SimpleImmutableEntry<>(k3, v3) }; return appendSpliterator(null, Spliterators.spliterator(array, Spliterator.ORDERED)); } /** * Returns a new {@code EntryStream} which is a concatenation of the stream * created from the supplied map entries and this stream. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate * operation</a> with <a href="package-summary.html#TSO">tail-stream * optimization</a>. * * <p> * May return this if the supplied map is empty and non-concurrent. * * @param map the map to prepend to the stream * @return the new stream * @since 0.2.1 */ public EntryStream<K, V> prepend(Map<K, V> map) { return prependSpliterator(null, map.entrySet().spliterator()); } /** * Returns a new {@code EntryStream} which is a concatenation of the * supplied key-value pair and this stream. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate * operation</a> with <a href="package-summary.html#TSO">tail-stream * optimization</a>. * * @param key the key of the new {@code Entry} to prepend to this stream * @param value the value of the new {@code Entry} to prepend to this stream * @return the new stream */ public EntryStream<K, V> prepend(K key, V value) { return supply(new PrependSpliterator<>(spliterator(), new SimpleImmutableEntry<>(key, value))); } /** * Returns a new {@code EntryStream} which is a concatenation of two * supplied key-value pairs and this stream. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate * operation</a> with <a href="package-summary.html#TSO">tail-stream * optimization</a>. * * @param k1 the key of the first {@code Entry} to prepend to this stream * @param v1 the value of the first {@code Entry} to prepend to this stream * @param k2 the key of the second {@code Entry} to prepend to this stream * @param v2 the value of the second {@code Entry} to prepend to this stream * @return the new stream * @since 0.2.3 */ public EntryStream<K, V> prepend(K k1, V v1, K k2, V v2) { @SuppressWarnings("unchecked") SimpleImmutableEntry<K, V>[] array = new SimpleImmutableEntry[] { new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2) }; return prependSpliterator(null, Spliterators.spliterator(array, Spliterator.ORDERED)); } /** * Returns a new {@code EntryStream} which is a concatenation of three * supplied key-value pairs and this stream. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate * operation</a> with <a href="package-summary.html#TSO">tail-stream * optimization</a>. * * @param k1 the key of the first {@code Entry} to prepend to this stream * @param v1 the value of the first {@code Entry} to prepend to this stream * @param k2 the key of the second {@code Entry} to prepend to this stream * @param v2 the value of the second {@code Entry} to prepend to this stream * @param k3 the key of the third {@code Entry} to prepend to this stream * @param v3 the value of the third {@code Entry} to prepend to this stream * @return the new stream * @since 0.2.3 */ public EntryStream<K, V> prepend(K k1, V v1, K k2, V v2, K k3, V v3) { @SuppressWarnings("unchecked") SimpleImmutableEntry<K, V>[] array = new SimpleImmutableEntry[] { new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2), new SimpleImmutableEntry<>(k3, v3) }; return prependSpliterator(null, Spliterators.spliterator(array, Spliterator.ORDERED)); } /** * Returns a stream consisting of the elements of this stream which have * distinct keys (according to object equality). * * <p> * For ordered streams, the selection of distinct keys is stable (for * elements with duplicating keys, the element appearing first in the * encounter order is preserved.) For unordered streams, no stability * guarantees are made. * * <p> * This is a <a href="package-summary.html#StreamOps">stateful intermediate * operation</a>. * * @return the new stream * @since 0.3.8 */ public EntryStream<K, V> distinctKeys() { return distinct(Entry::getKey); } /** * Returns a stream consisting of the elements of this stream which have * distinct values (according to object equality). * * <p> * For ordered streams, the selection of distinct values is stable (for * elements with duplicating values, the element appearing first in the * encounter order is preserved.) For unordered streams, no stability * guarantees are made. * * <p> * This is a <a href="package-summary.html#StreamOps">stateful intermediate * operation</a>. * * @return the new stream * @since 0.3.8 */ public EntryStream<K, V> distinctValues() { return distinct(Entry::getValue); } /** * Returns an {@code EntryStream} consisting of the entries whose keys are * modified by applying the given function and values are left unchanged. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param <KK> The type of the keys of the new stream * @param keyMapper a non-interfering, stateless function to apply to each * key * @return the new stream */ public <KK> EntryStream<KK, V> mapKeys(Function<? super K, ? extends KK> keyMapper) { return new EntryStream<>(stream().map( e -> new SimpleImmutableEntry<>(keyMapper.apply(e.getKey()), e.getValue())), context); } /** * Returns an {@code EntryStream} consisting of the entries whose keys are * left unchanged and values are modified by applying the given function. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param <VV> The type of the values of the new stream * @param valueMapper a non-interfering, stateless function to apply to each * value * @return the new stream */ public <VV> EntryStream<K, VV> mapValues(Function<? super V, ? extends VV> valueMapper) { return new EntryStream<>(stream().map( e -> new SimpleImmutableEntry<>(e.getKey(), valueMapper.apply(e.getValue()))), context); } /** * Returns a {@link StreamEx} consisting of the results of applying the * given function to the keys and values of this stream. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param <R> The element type of the new stream * @param mapper a non-interfering, stateless function to apply to key and * value of each {@link Entry} in this stream * @return the new stream */ public <R> StreamEx<R> mapKeyValue(BiFunction<? super K, ? super V, ? extends R> mapper) { return this.<R> map(toFunction(mapper)); } /** * Returns an {@code EntryStream} consisting of the entries whose keys are * modified by applying the given function and values are left unchanged. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param <KK> The type of the keys of the new stream * @param keyMapper a non-interfering, stateless function to apply to each * key-value pair which returns the updated key * @return the new stream * @since 0.3.0 */ public <KK> EntryStream<KK, V> mapToKey(BiFunction<? super K, ? super V, ? extends KK> keyMapper) { return new EntryStream<>(stream().map( e -> new SimpleImmutableEntry<>(keyMapper.apply(e.getKey(), e.getValue()), e.getValue())), context); } /** * Returns an {@code EntryStream} consisting of the entries whose keys are * left unchanged and values are modified by applying the given function. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param <VV> The type of the values of the new stream * @param valueMapper a non-interfering, stateless function to apply to each * key-value pair which returns the updated value * @return the new stream * @since 0.3.0 */ public <VV> EntryStream<K, VV> mapToValue(BiFunction<? super K, ? super V, ? extends VV> valueMapper) { return new EntryStream<>(stream().map( e -> new SimpleImmutableEntry<>(e.getKey(), valueMapper.apply(e.getKey(), e.getValue()))), context); } /** * Returns a stream consisting of the {@link Entry} objects which keys are * the values of this stream elements and vice versa. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @return the new stream */ public EntryStream<V, K> invert() { return new EntryStream<>(stream().map(e -> new SimpleImmutableEntry<>(e.getValue(), e.getKey())), context); } /** * Returns a stream consisting of the elements of this stream which keys * match the given predicate. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param keyPredicate a non-interfering, stateless predicate to apply to * the key of each element to determine if it should be included * @return the new stream */ public EntryStream<K, V> filterKeys(Predicate<? super K> keyPredicate) { return filter(e -> keyPredicate.test(e.getKey())); } /** * Returns a stream consisting of the elements of this stream which values * match the given predicate. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param valuePredicate a non-interfering, stateless predicate to apply to * the value of each element to determine if it should be included * @return the new stream */ public EntryStream<K, V> filterValues(Predicate<? super V> valuePredicate) { return filter(e -> valuePredicate.test(e.getValue())); } /** * Returns a stream consisting of the elements of this stream which elements * match the given predicate. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param predicate a non-interfering, stateless predicate to apply to the * key-value pairs of each element to determine if it should be * included * @return the new stream * @since 0.3.0 */ public EntryStream<K, V> filterKeyValue(BiPredicate<? super K, ? super V> predicate) { return filter(e -> predicate.test(e.getKey(), e.getValue())); } /** * Returns a stream consisting of the elements of this stream which keys * don't match the given predicate. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param keyPredicate a non-interfering, stateless predicate to apply to * the key of each element to determine if it should be excluded * @return the new stream */ public EntryStream<K, V> removeKeys(Predicate<? super K> keyPredicate) { return filterKeys(keyPredicate.negate()); } /** * Returns a stream consisting of the elements of this stream which values * don't match the given predicate. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param valuePredicate a non-interfering, stateless predicate to apply to * the value of each element to determine if it should be excluded * @return the new stream */ public EntryStream<K, V> removeValues(Predicate<? super V> valuePredicate) { return filterValues(valuePredicate.negate()); } /** * Returns a stream consisting of the elements of this stream which values * don't match the given predicate. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param predicate a non-interfering, stateless predicate to apply to the * key-value pairs of each element to determine if it should be * excluded * @return the new stream * @since 0.6.0 */ public EntryStream<K, V> removeKeyValue(BiPredicate<? super K, ? super V> predicate) { return filterKeyValue(predicate.negate()); } /** * Returns a stream consisting of the elements of this stream which key is * not null. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @return the new stream */ public EntryStream<K, V> nonNullKeys() { return filter(e -> e.getKey() != null); } /** * Returns a stream consisting of the elements of this stream which value is * not null. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @return the new stream */ public EntryStream<K, V> nonNullValues() { return filter(e -> e.getValue() != null); } /** * Returns a stream consisting of the elements of this stream which keys are * instances of given class. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param <KK> a type of keys to select. * @param clazz a class to filter the keys. * @return the new stream */ @SuppressWarnings({ "unchecked" }) public <KK> EntryStream<KK, V> selectKeys(Class<KK> clazz) { return (EntryStream<KK, V>) filter(e -> clazz.isInstance(e.getKey())); } /** * Returns a stream consisting of the elements of this stream which values * are instances of given class. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @param <VV> a type of values to select. * @param clazz a class to filter the values. * @return the new stream */ @SuppressWarnings({ "unchecked" }) public <VV> EntryStream<K, VV> selectValues(Class<VV> clazz) { return (EntryStream<K, VV>) filter(e -> clazz.isInstance(e.getValue())); } /** * Returns a stream consisting of the entries of this stream, additionally * performing the provided action on each entry key as entries are consumed * from the resulting stream. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * <p> * For parallel stream pipelines, the action may be called at whatever time * and in whatever thread the element is made available by the upstream * operation. If the action modifies shared state, it is responsible for * providing the required synchronization. * * @param keyAction a non-interfering action to perform on the keys of the * entries as they are consumed from the stream * @return the new stream * @since 0.2.3 */ public EntryStream<K, V> peekKeys(Consumer<? super K> keyAction) { return peek(e -> keyAction.accept(e.getKey())); } /** * Returns a stream consisting of the entries of this stream, additionally * performing the provided action on each entry value as entries are * consumed from the resulting stream. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * <p> * For parallel stream pipelines, the action may be called at whatever time * and in whatever thread the element is made available by the upstream * operation. If the action modifies shared state, it is responsible for * providing the required synchronization. * * @param valueAction a non-interfering action to perform on the values of * the entries as they are consumed from the stream * @return the new stream * @since 0.2.3 */ public EntryStream<K, V> peekValues(Consumer<? super V> valueAction) { return peek(e -> valueAction.accept(e.getValue())); } /** * Returns a stream consisting of the entries of this stream, additionally * performing the provided action on each entry key-value pair as entries * are consumed from the resulting stream. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * <p> * For parallel stream pipelines, the action may be called at whatever time * and in whatever thread the element is made available by the upstream * operation. If the action modifies shared state, it is responsible for * providing the required synchronization. * * @param action a non-interfering action to perform on the keys and values * of the entries as they are consumed from the stream * @return the new stream * @since 0.2.3 */ public EntryStream<K, V> peekKeyValue(BiConsumer<? super K, ? super V> action) { return peek(toConsumer(action)); } /** * Returns a stream consisting of the keys of this stream elements. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @return the new stream */ public StreamEx<K> keys() { return map(Entry::getKey); } /** * Returns a stream consisting of the values of this stream elements. * * <p> * This is an <a href="package-summary.html#StreamOps">intermediate</a> * operation. * * @return the new stream */ public StreamEx<V> values() { return map(Entry::getValue); } /** * Merge series of adjacent stream entries with equal keys grouping the * corresponding values into {@code List}. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate</a> * partial reduction operation. * * <p> * There are no guarantees on the type, mutability, serializability, or * thread-safety of the {@code List} objects of the resulting stream. * * <p> * The key of the resulting entry is the key of the first merged entry. * * @return a new {@code EntryStream} which keys are the keys of the original * stream and the values of adjacent entries with the same keys are * grouped into {@code List} * @see StreamEx#groupRuns(BiPredicate) * @since 0.5.5 */ public EntryStream<K, List<V>> collapseKeys() { return new StreamEx<>(new CollapseSpliterator<Entry<K, V>, PairBox<K, List<V>>>(equalKeys(), e -> new PairBox<>(e.getKey(), Collections.singletonList(e.getValue())), (pb, e) -> { if (!(pb.b instanceof ArrayList)) { V old = pb.b.get(0); pb.b = new ArrayList<>(); pb.b.add(old); } pb.b.add(e.getValue()); return pb; }, (pb1, pb2) -> { if (!(pb1.b instanceof ArrayList)) { V old = pb1.b.get(0); pb1.b = new ArrayList<>(); pb1.b.add(old); } pb1.b.addAll(pb2.b); return pb1; }, spliterator()), context).mapToEntry(pb -> pb.a, pb -> pb.b); } /** * Merge series of adjacent stream entries with equal keys combining the * corresponding values using the provided function. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate</a> * partial reduction operation. * * <p> * The key of the resulting entry is the key of the first merged entry. * * @param merger a non-interfering, stateless, associative function to merge * values of two adjacent entries which keys are equal. Note that it * can be applied to the results if previous merges. * @return a new {@code EntryStream} which keys are the keys of the original * stream and the values are values of the adjacent entries with the * same keys, combined using the provided merger function. * @see StreamEx#collapse(BiPredicate, BinaryOperator) * @since 0.5.5 */ public EntryStream<K, V> collapseKeys(BinaryOperator<V> merger) { BinaryOperator<Entry<K, V>> entryMerger = (e1, e2) -> new SimpleImmutableEntry<>(e1.getKey(), merger.apply(e1 .getValue(), e2.getValue())); return new EntryStream<>(new CollapseSpliterator<>(equalKeys(), Function.identity(), entryMerger, entryMerger, spliterator()), context); } /** * Merge series of adjacent stream entries with equal keys combining the * corresponding values using the provided {@code Collector}. * * <p> * This is a <a href="package-summary.html#StreamOps">quasi-intermediate</a> * partial reduction operation. * * <p> * The key of the resulting entry is the key of the first merged entry. * * @param <R> the type of the values in the resulting stream * @param <A> the intermediate accumulation type of the {@code Collector} * @param collector a {@code Collector} which is used to combine the values * of the adjacent entries with the equal keys. * @return a new {@code EntryStream} which keys are the keys of the original * stream and the values are values of the adjacent entries with the * same keys, combined using the provided collector. * @see StreamEx#collapse(BiPredicate, Collector) * @since 0.5.5 */ public <A, R> EntryStream<K, R> collapseKeys(Collector<? super V, A, R> collector) { Supplier<A> supplier = collector.supplier(); BiConsumer<A, ? super V> accumulator = collector.accumulator(); BinaryOperator<A> combiner = collector.combiner(); Function<A, R> finisher = collector.finisher(); return new StreamEx<>(new CollapseSpliterator<Entry<K, V>, PairBox<K, A>>(equalKeys(), e -> { A a = supplier.get(); accumulator.accept(a, e.getValue()); return new PairBox<>(e.getKey(), a); }, (pb, e) -> { accumulator.accept(pb.b, e.getValue()); return pb; }, (pb1, pb2) -> { pb1.b = combiner.apply(pb1.b, pb2.b); return pb1; }, spliterator()), context).mapToEntry(pb -> pb.a, pb -> finisher.apply(pb.b)); } /** * Returns a new {@code EntryStream} which values are the same as this * stream values and keys are the results of applying the accumulation * function to this stream keys, going left to right. * * <p> * This is a stateful * <a href="package-summary.html#StreamOps">quasi-intermediate</a> * operation. * * <p> * This method cannot take all the advantages of parallel streams as it must * process elements strictly left to right. Using an unordered source or * removing the ordering constraint with {@link #unordered()} may improve * the parallel processing speed. * * @param op an <a href="package-summary.html#Associativity">associative</a> * , <a href="package-summary.html#NonInterference">non-interfering * </a>, <a href="package-summary.html#Statelessness">stateless</a> * function for computing the next key based on the previous one * @return the new stream. * @see #prefix(BinaryOperator) * @see #prefixValues(BinaryOperator) * @since 0.6.4 */ public EntryStream<K, V> prefixKeys(BinaryOperator<K> op) { return prefix((a, b) -> new SimpleImmutableEntry<>(op.apply(a.getKey(), b.getKey()), b.getValue())); } /** * Returns a new {@code EntryStream} which keys are the same as this stream * keys and values are the results of applying the accumulation function to * this stream values, going left to right. * * <p> * This is a stateful * <a href="package-summary.html#StreamOps">quasi-intermediate</a> * operation. * * <p> * This method cannot take all the advantages of parallel streams as it must * process elements strictly left to right. Using an unordered source or * removing the ordering constraint with {@link #unordered()} may improve * the parallel processing speed. * * @param op an <a href="package-summary.html#Associativity">associative</a> * , <a href="package-summary.html#NonInterference">non-interfering * </a>, <a href="package-summary.html#Statelessness">stateless</a> * function for computing the next value based on the previous one * @return the new stream. * @see #prefix(BinaryOperator) * @see #prefixKeys(BinaryOperator) * @since 0.6.4 */ public EntryStream<K, V> prefixValues(BinaryOperator<V> op) { return prefix((a, b) -> new SimpleImmutableEntry<>(b.getKey(), op.apply(a.getValue(), b.getValue()))); } /** * Returns a {@link Map} containing the elements of this stream. There are * no guarantees on the type or serializability of the {@code Map} returned; * if more control over the returned {@code Map} is required, use * {@link #toCustomMap(Supplier)}. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * <p> * Returned {@code Map} is guaranteed to be modifiable. * * <p> * For parallel stream the concurrent {@code Map} is created. * * @return a {@code Map} containing the elements of this stream * @throws IllegalStateException if this stream contains duplicate keys * (according to {@link Object#equals(Object)}) * @see Collectors#toMap(Function, Function) * @see Collectors#toConcurrentMap(Function, Function) * @see #toImmutableMap() */ public Map<K, V> toMap() { Map<K, V> map = isParallel() ? new ConcurrentHashMap<>() : new HashMap<>(); forEach(toMapConsumer(map)); return map; } /** * Returns an immutable {@link Map} containing the elements of this stream. * There's no guarantees on exact type of the returned {@code Map}. In * particular, no specific element order in the resulting {@code Map} is * guaranteed. The returned {@code Map} is guaranteed to be serializable if * all its elements are serializable. * * <p> * This is a terminal operation. * * @return a {@code Map} containing the elements of this stream * @throws IllegalStateException if this stream contains duplicate keys * (according to {@link Object#equals(Object)}) * @see #toMap() * @since 0.6.3 */ public Map<K, V> toImmutableMap() { Map<K, V> map = toMap(); return map.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(map); } /** * Creates a {@link Map} containing the elements of this stream, then * performs finishing transformation and returns its result. There are no * guarantees on the type or serializability of the {@code Map} created. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * <p> * Created {@code Map} is guaranteed to be modifiable. * * <p> * For parallel stream the concurrent {@code Map} is created. * * @param <R> the type of the result * @param finisher a function to be applied to the intermediate map * @return result of applying the finisher transformation to the {@code Map} * of the stream elements. * @throws IllegalStateException if this stream contains duplicate keys * (according to {@link Object#equals(Object)}) * @see #toMap() * @since 0.5.5 */ public <R> R toMapAndThen(Function<? super Map<K, V>, R> finisher) { if (context.fjp != null) return context.terminate(() -> finisher.apply(toMap())); return finisher.apply(toMap()); } /** * Returns a {@link Map} containing the elements of this stream. There are * no guarantees on the type or serializability of the {@code Map} returned; * if more control over the returned {@code Map} is required, use * {@link #toCustomMap(BinaryOperator, Supplier)}. * * <p> * If the mapped keys contains duplicates (according to * {@link Object#equals(Object)}), the value mapping function is applied to * each equal element, and the results are merged using the provided merging * function. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * <p> * Returned {@code Map} is guaranteed to be modifiable. * * @param mergeFunction a merge function, used to resolve collisions between * values associated with the same key, as supplied to * {@link Map#merge(Object, Object, BiFunction)} * @return a {@code Map} containing the elements of this stream * @see Collectors#toMap(Function, Function) * @see Collectors#toConcurrentMap(Function, Function) * @since 0.1.0 */ public Map<K, V> toMap(BinaryOperator<V> mergeFunction) { Function<Entry<K, V>, K> keyMapper = Entry::getKey; Function<Entry<K, V>, V> valueMapper = Entry::getValue; return collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction, HashMap::new)); } /** * Returns a {@link Map} containing the elements of this stream. The * {@code Map} is created by a provided supplier function. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * @param <M> the type of the resulting map * @param mapSupplier a function which returns a new, empty {@code Map} into * which the results will be inserted * @return a {@code Map} containing the elements of this stream * @throws IllegalStateException if this stream contains duplicate keys * (according to {@link Object#equals(Object)}) * @see Collectors#toMap(Function, Function) * @see Collectors#toConcurrentMap(Function, Function) */ public <M extends Map<K, V>> M toCustomMap(Supplier<M> mapSupplier) { M map = mapSupplier.get(); if (isParallel() && !(map instanceof ConcurrentMap)) { return collect(mapSupplier, (m, t) -> addToMap(m, t.getKey(), Objects.requireNonNull(t.getValue())), (m1, m2) -> m2.forEach((k, v) -> addToMap(m1, k, v))); } forEach(toMapConsumer(map)); return map; } /** * Returns a {@link Map} containing the elements of this stream. The * {@code Map} is created by a provided supplier function. * * <p> * If the mapped keys contains duplicates (according to * {@link Object#equals(Object)}), the value mapping function is applied to * each equal element, and the results are merged using the provided merging * function. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * @param <M> the type of the resulting map * @param mergeFunction a merge function, used to resolve collisions between * values associated with the same key. * @param mapSupplier a function which returns a new, empty {@code Map} into * which the results will be inserted * @return a {@code Map} containing the elements of this stream * @see Collectors#toMap(Function, Function) * @see Collectors#toConcurrentMap(Function, Function) */ public <M extends Map<K, V>> M toCustomMap(BinaryOperator<V> mergeFunction, Supplier<M> mapSupplier) { Function<Entry<K, V>, K> keyMapper = Entry::getKey; Function<Entry<K, V>, V> valueMapper = Entry::getValue; return collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)); } /** * Returns a {@link SortedMap} containing the elements of this stream. There * are no guarantees on the type or serializability of the {@code SortedMap} * returned; if more control over the returned {@code Map} is required, use * {@link #toCustomMap(Supplier)}. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * <p> * Returned {@code SortedMap} is guaranteed to be modifiable. * * <p> * For parallel stream the concurrent {@code SortedMap} is created. * * @return a {@code SortedMap} containing the elements of this stream * @see #toNavigableMap() * @throws IllegalStateException if this stream contains duplicate keys * (according to {@link Object#equals(Object)}) * @since 0.1.0 */ public SortedMap<K, V> toSortedMap() { return toNavigableMap(); } /** * Returns a {@link SortedMap} containing the elements of this stream. There * are no guarantees on the type or serializability of the {@code SortedMap} * returned; if more control over the returned {@code Map} is required, use * {@link #toCustomMap(BinaryOperator, Supplier)}. * * <p> * If the mapped keys contains duplicates (according to * {@link Object#equals(Object)}), the value mapping function is applied to * each equal element, and the results are merged using the provided merging * function. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * <p> * Returned {@code SortedMap} is guaranteed to be modifiable. * * @param mergeFunction a merge function, used to resolve collisions between * values associated with the same key, as supplied to * {@link Map#merge(Object, Object, BiFunction)} * @return a {@code SortedMap} containing the elements of this stream * @see #toNavigableMap(BinaryOperator) * @since 0.1.0 */ public SortedMap<K, V> toSortedMap(BinaryOperator<V> mergeFunction) { return toNavigableMap(mergeFunction); } /** * Returns a {@link NavigableMap} containing the elements of this stream. * There are no guarantees on the type or serializability of the * {@code NavigableMap} returned; if more control over the returned * {@code Map} is required, use {@link #toCustomMap(Supplier)}. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * <p> * Returned {@code NavigableMap} is guaranteed to be modifiable. * * <p> * For parallel stream the concurrent {@code NavigableMap} is created. * * @return a {@code NavigableMap} containing the elements of this stream * @see Collectors#toMap(Function, Function) * @see Collectors#toConcurrentMap(Function, Function) * @throws IllegalStateException if this stream contains duplicate keys * (according to {@link Object#equals(Object)}) * @since 0.6.5 */ public NavigableMap<K, V> toNavigableMap() { NavigableMap<K, V> map = isParallel() ? new ConcurrentSkipListMap<>() : new TreeMap<>(); forEach(toMapConsumer(map)); return map; } /** * Returns a {@link NavigableMap} containing the elements of this stream. * There are no guarantees on the type or serializability of the * {@code NavigableMap} returned; if more control over the returned * {@code Map} is required, use * {@link #toCustomMap(BinaryOperator, Supplier)}. * * <p> * If the mapped keys contains duplicates (according to * {@link Object#equals(Object)}), the value mapping function is applied to * each equal element, and the results are merged using the provided merging * function. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * <p> * Returned {@code NavigableMap} is guaranteed to be modifiable. * * @param mergeFunction a merge function, used to resolve collisions between * values associated with the same key, as supplied to * {@link Map#merge(Object, Object, BiFunction)} * @return a {@code NavigableMap} containing the elements of this stream * @see Collectors#toMap(Function, Function) * @since 0.6.5 */ public NavigableMap<K, V> toNavigableMap(BinaryOperator<V> mergeFunction) { return collect(Collectors.toMap(Entry::getKey, Entry::getValue, mergeFunction, TreeMap::new)); } /** * Drains the stream content into the supplied {@code Map}. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * @param <M> type of the resulting map * @param map a mutable map to put the stream elements into * @return the supplied map, updated from this stream * @throws IllegalStateException if this stream contains duplicate keys, or * the stream contains the key which was already present in the map * (according to {@link Object#equals(Object)}) * @since 0.6.3 */ public <M extends Map<K, V>> M into(M map) { Consumer<? super Entry<K, V>> cons = toMapConsumer(map); if (isParallel()) { if (map instanceof ConcurrentMap) forEach(cons); else toMap().entrySet().forEach(cons); } else { spliterator().forEachRemaining(cons); } return map; } /** * Returns a {@link Map} where elements of this stream with the same key are * grouped together. The resulting {@code Map} keys are the keys of this * stream entries and the values are the lists of the corresponding values. * * <p> * There are no guarantees on the type, mutability, serializability, or * thread-safety of the {@code Map} or {@code List} objects returned. If * more control over the returned {@code Map} is required, use * {@link #grouping(Supplier)}. If more control over the lists required, use * {@link #groupingTo(Supplier)}. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * @return a {@code Map} containing the elements of this stream * @see Collectors#groupingBy(Function) */ public Map<K, List<V>> grouping() { return grouping(Collectors.toList()); } /** * Returns a {@link Map} where elements of this stream with the same key are * grouped together. The resulting {@code Map} keys are the keys of this * stream entries and the values are the lists of the corresponding values. * The {@code Map} is created using the provided supplier function. * * <p> * There are no guarantees on the type, mutability, serializability, or * thread-safety of the {@code List} objects returned. If more control over * the lists required, use {@link #groupingTo(Supplier)}. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * @param <M> the type of the resulting {@code Map} * @param mapSupplier a function which returns a new, empty {@code Map} into * which the results will be inserted * @return a {@code Map} containing the elements of this stream * @see #grouping(Supplier, Collector) * @see #groupingTo(Supplier, Supplier) */ public <M extends Map<K, List<V>>> M grouping(Supplier<M> mapSupplier) { return grouping(mapSupplier, Collectors.toList()); } /** * Returns a {@link Map} where elements of this stream with the same key are * grouped together. The resulting {@code Map} keys are the keys of this * stream entries and the corresponding values are combined using the * provided downstream collector. * * <p> * There are no guarantees on the type, mutability, serializability, or * thread-safety of the {@code Map} object returned. If more control over * the returned {@code Map} is required, use * {@link #grouping(Supplier, Collector)}. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * @param <A> the intermediate accumulation type of the downstream collector * @param <D> the result type of the downstream reduction * @param downstream a {@code Collector} implementing the downstream * reduction * @return a {@code Map} containing the elements of this stream * @see Collectors#groupingBy(Function, Collector) */ public <A, D> Map<K, D> grouping(Collector<? super V, A, D> downstream) { Function<Entry<K, V>, K> keyMapper = Entry::getKey; Collector<Entry<K, V>, ?, D> mapping = Collectors.mapping(Entry::getValue, downstream); if (isParallel() && downstream.characteristics().contains(Characteristics.UNORDERED)) { return collect(Collectors.groupingByConcurrent(keyMapper, mapping)); } return collect(Collectors.groupingBy(keyMapper, mapping)); } /** * Returns a {@link Map} where elements of this stream with the same key are * grouped together. The resulting {@code Map} keys are the keys of this * stream entries and the corresponding values are combined using the * provided downstream collector. The {@code Map} is created using * the provided supplier function. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * @param <A> the intermediate accumulation type of the downstream collector * @param <D> the result type of the downstream reduction * @param <M> the type of the resulting {@code Map} * @param mapSupplier a function which returns a new, empty {@code Map} into * which the results will be inserted * @param downstream a {@code Collector} implementing the downstream * reduction * @return a {@code Map} containing the elements of this stream * @see Collectors#groupingBy(Function, Supplier, Collector) */ @SuppressWarnings("unchecked") public <A, D, M extends Map<K, D>> M grouping(Supplier<M> mapSupplier, Collector<? super V, A, D> downstream) { Function<Entry<K, V>, K> keyMapper = Entry::getKey; Collector<Entry<K, V>, ?, D> mapping = Collectors.mapping(Entry::getValue, downstream); if (isParallel() && downstream.characteristics().contains(Characteristics.UNORDERED) && mapSupplier.get() instanceof ConcurrentMap) { return (M) collect(Collectors.groupingByConcurrent(keyMapper, (Supplier<? extends ConcurrentMap<K, D>>) mapSupplier, mapping)); } return collect(Collectors.groupingBy(keyMapper, mapSupplier, mapping)); } /** * Returns a {@link Map} where elements of this stream with the same key are * grouped together. The resulting {@code Map} keys are the keys of this * stream entries and the values are the collections of the corresponding * values. The collections are created by the provided factory. * * <p> * There are no guarantees on the type, mutability, serializability, or * thread-safety of the {@code Map} object returned. If more control over * the returned {@code Map} is required, use * {@link #groupingTo(Supplier, Supplier)}. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * @param <C> the type of the resulting {@code Collection} * @param collectionFactory a {@code Supplier} which returns a new, empty * {@code Collection} of the appropriate type * @return a {@code Map} containing the elements of this stream * @see Collectors#toCollection(Supplier) */ public <C extends Collection<V>> Map<K, C> groupingTo(Supplier<C> collectionFactory) { return grouping(Collectors.toCollection(collectionFactory)); } /** * Returns a {@link Map} where elements of this stream with the same key are * grouped together. The resulting {@code Map} keys are the keys of this * stream entries and the values are the collections of the corresponding * values. The collections are created by the provided factory. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * @param <C> the type of the resulting {@code Collection} * @param <M> the type of the resulting {@code Map} * @param mapSupplier a function which returns a new, empty {@code Map} into * which the results will be inserted * @param collectionFactory a {@code Supplier} which returns a new, empty * {@code Collection} of the appropriate type * @return a {@code Map} containing the elements of this stream * @see Collectors#toCollection(Supplier) */ public <C extends Collection<V>, M extends Map<K, C>> M groupingTo(Supplier<M> mapSupplier, Supplier<C> collectionFactory) { return grouping(mapSupplier, Collectors.toCollection(collectionFactory)); } /** * Performs an action for each key-value pair of this stream. * * <p> * This is a <a href="package-summary.html#StreamOps">terminal</a> * operation. * * <p> * The behavior of this operation is explicitly nondeterministic. For * parallel stream pipelines, this operation does <em>not</em> guarantee to * respect the encounter order of the stream, as doing so would sacrifice * the benefit of parallelism. For any given element, the action may be * performed at whatever time and in whatever thread the library chooses. If * the action accesses shared state, it is responsible for providing the * required synchronization. * * @param action a non-interfering action to perform on the key and value * @see #forEach(java.util.function.Consumer) */ public void forKeyValue(BiConsumer<? super K, ? super V> action) { forEach(toConsumer(action)); } /** * Returns an empty sequential {@code EntryStream}. * * @param <K> the type of stream element keys * @param <V> the type of stream element values * @return an empty sequential stream * @since 0.0.8 */ public static <K, V> EntryStream<K, V> empty() { return of(Stream.empty()); } /** * Returns an {@code EntryStream} object which wraps given {@link Stream} of * {@link Entry} elements * * @param <K> the type of original stream keys * @param <V> the type of original stream values * @param stream original stream * @return the wrapped stream */ public static <K, V> EntryStream<K, V> of(Stream<? extends Entry<K, V>> stream) { if (stream instanceof AbstractStreamEx) { @SuppressWarnings("unchecked") AbstractStreamEx<Entry<K, V>, ?> ase = (AbstractStreamEx<Entry<K, V>, ?>) stream; if (ase.spliterator != null) return new EntryStream<>(ase.spliterator(), ase.context); return new EntryStream<>(ase.stream(), ase.context); } return new EntryStream<>(stream, StreamContext.of(stream)); } /** * Returns a sequential {@link EntryStream} created from given * {@link Spliterator}. * * @param <K> the type of stream keys * @param <V> the type of stream values * @param spliterator a spliterator to create the stream from. * @return the new stream * @since 0.3.4 */ public static <K, V> EntryStream<K, V> of(Spliterator<? extends Entry<K, V>> spliterator) { return of(StreamSupport.stream(spliterator, false)); } /** * Returns a sequential, ordered {@link EntryStream} created from given * {@link Iterator}. * * <p> * This method is roughly equivalent to * {@code EntryStream.of(Spliterators.spliteratorUnknownSize(iterator, ORDERED))} * , but may show better performance for parallel processing. * * <p> * Use this method only if you cannot provide better Stream source (like * {@code Collection} or {@code Spliterator}). * * @param <K> the type of stream keys * @param <V> the type of stream values * @param iterator an iterator to create the stream from. * @return the new stream * @since 0.5.1 */ public static <K, V> EntryStream<K, V> of(Iterator<? extends Entry<K, V>> iterator) { return of(new UnknownSizeSpliterator.USOfRef<>(iterator)); } /** * Returns an {@code EntryStream} object which contains the entries of * supplied {@code Map}. * * @param <K> the type of map keys * @param <V> the type of map values * @param map the map to create the stream from * @return a new {@code EntryStream} */ public static <K, V> EntryStream<K, V> of(Map<K, V> map) { return of(map.entrySet().stream()); } /** * Returns an {@code EntryStream} object whose keys are indices of given * list and the values are the corresponding list elements. * * <p> * The list elements are accessed using {@link List#get(int)}, so the list * should provide fast random access. The list is assumed to be unmodifiable * during the stream operations. * * @param <V> list element type * @param list list to create the stream from * @return a new {@code EntryStream} * @since 0.2.3 */ public static <V> EntryStream<Integer, V> of(List<V> list) { return EntryStream.of(new RangeBasedSpliterator.AsEntry<>(list)); } /** * Returns an {@code EntryStream} object whose keys are indices of given * array and the values are the corresponding array elements. * * @param <V> array element type * @param array array to create the stream from * @return a new {@code EntryStream} * @since 0.2.3 */ public static <V> EntryStream<Integer, V> of(V[] array) { return of(Arrays.asList(array)); } /** * Returns a sequential {@code EntryStream} containing a single key-value * pair * * @param <K> the type of key * @param <V> the type of value * @param key the key of the single element * @param value the value of the single element * @return a singleton sequential stream */ public static <K, V> EntryStream<K, V> of(K key, V value) { return of(Stream.of(new SimpleImmutableEntry<>(key, value))); } /** * Returns a sequential {@code EntryStream} containing two key-value pairs * * @param <K> the type of key * @param <V> the type of value * @param k1 the key of the first element * @param v1 the value of the first element * @param k2 the key of the second element * @param v2 the value of the second element * @return a sequential stream * @since 0.2.3 */ public static <K, V> EntryStream<K, V> of(K k1, V v1, K k2, V v2) { return of(Stream.of(new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2))); } /** * Returns a sequential {@code EntryStream} containing three key-value pairs * * @param <K> the type of key * @param <V> the type of value * @param k1 the key of the first element * @param v1 the value of the first element * @param k2 the key of the second element * @param v2 the value of the second element * @param k3 the key of the third element * @param v3 the value of the third element * @return a sequential stream * @since 0.2.3 */ public static <K, V> EntryStream<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) { return of(Stream.of(new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2), new SimpleImmutableEntry<>(k3, v3))); } /** * Returns a sequential {@code EntryStream} containing four key-value pairs * * @param <K> the type of key * @param <V> the type of value * @param k1 the key of the first element * @param v1 the value of the first element * @param k2 the key of the second element * @param v2 the value of the second element * @param k3 the key of the third element * @param v3 the value of the third element * @param k4 the key of the fourth element * @param v4 the value of the fourth element * @return a sequential stream * @since 0.5.2 */ public static <K, V> EntryStream<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { return of(Stream.of(new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2), new SimpleImmutableEntry<>(k3, v3), new SimpleImmutableEntry<>(k4, v4))); } /** * Returns a sequential {@code EntryStream} containing five key-value pairs * * @param <K> the type of key * @param <V> the type of value * @param k1 the key of the first element * @param v1 the value of the first element * @param k2 the key of the second element * @param v2 the value of the second element * @param k3 the key of the third element * @param v3 the value of the third element * @param k4 the key of the fourth element * @param v4 the value of the fourth element * @param k5 the key of the fifth element * @param v5 the value of the fifth element * @return a sequential stream * @since 0.5.2 */ public static <K, V> EntryStream<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { return of(Stream.of(new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2), new SimpleImmutableEntry<>(k3, v3), new SimpleImmutableEntry<>(k4, v4), new SimpleImmutableEntry<>(k5, v5))); } /** * Returns a sequential {@code EntryStream} containing six key-value pairs * * @param <K> the type of key * @param <V> the type of value * @param k1 the key of the first element * @param v1 the value of the first element * @param k2 the key of the second element * @param v2 the value of the second element * @param k3 the key of the third element * @param v3 the value of the third element * @param k4 the key of the fourth element * @param v4 the value of the fourth element * @param k5 the key of the fifth element * @param v5 the value of the fifth element * @param k6 the key of the sixth element * @param v6 the value of the sixth element * @return a sequential stream * @since 0.5.2 */ public static <K, V> EntryStream<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { return of(Stream.of(new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2), new SimpleImmutableEntry<>(k3, v3), new SimpleImmutableEntry<>(k4, v4), new SimpleImmutableEntry<>(k5, v5), new SimpleImmutableEntry<>(k6, v6))); } /** * Returns a sequential {@code EntryStream} containing seven key-value pairs * * @param <K> the type of key * @param <V> the type of value * @param k1 the key of the first element * @param v1 the value of the first element * @param k2 the key of the second element * @param v2 the value of the second element * @param k3 the key of the third element * @param v3 the value of the third element * @param k4 the key of the fourth element * @param v4 the value of the fourth element * @param k5 the key of the fifth element * @param v5 the value of the fifth element * @param k6 the key of the sixth element * @param v6 the value of the sixth element * @param k7 the key of the seventh element * @param v7 the value of the seventh element * @return a sequential stream * @since 0.5.2 */ public static <K, V> EntryStream<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { return of(Stream.of(new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2), new SimpleImmutableEntry<>(k3, v3), new SimpleImmutableEntry<>(k4, v4), new SimpleImmutableEntry<>(k5, v5), new SimpleImmutableEntry<>(k6, v6), new SimpleImmutableEntry<>(k7, v7))); } /** * Returns a sequential {@code EntryStream} containing eight key-value pairs * * @param <K> the type of key * @param <V> the type of value * @param k1 the key of the first element * @param v1 the value of the first element * @param k2 the key of the second element * @param v2 the value of the second element * @param k3 the key of the third element * @param v3 the value of the third element * @param k4 the key of the fourth element * @param v4 the value of the fourth element * @param k5 the key of the fifth element * @param v5 the value of the fifth element * @param k6 the key of the sixth element * @param v6 the value of the sixth element * @param k7 the key of the seventh element * @param v7 the value of the seventh element * @param k8 the key of the eighth element * @param v8 the value of the eighth element * @return a sequential stream * @since 0.5.2 */ public static <K, V> EntryStream<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8) { return of(Stream.of(new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2), new SimpleImmutableEntry<>(k3, v3), new SimpleImmutableEntry<>(k4, v4), new SimpleImmutableEntry<>(k5, v5), new SimpleImmutableEntry<>(k6, v6), new SimpleImmutableEntry<>(k7, v7), new SimpleImmutableEntry<>(k8, v8))); } /** * Returns a sequential {@code EntryStream} containing nine key-value pairs * * @param <K> the type of key * @param <V> the type of value * @param k1 the key of the first element * @param v1 the value of the first element * @param k2 the key of the second element * @param v2 the value of the second element * @param k3 the key of the third element * @param v3 the value of the third element * @param k4 the key of the fourth element * @param v4 the value of the fourth element * @param k5 the key of the fifth element * @param v5 the value of the fifth element * @param k6 the key of the sixth element * @param v6 the value of the sixth element * @param k7 the key of the seventh element * @param v7 the value of the seventh element * @param k8 the key of the eighth element * @param v8 the value of the eighth element * @param k9 the key of the ninth element * @param v9 the value of the ninth element * @return a sequential stream * @since 0.5.2 */ public static <K, V> EntryStream<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) { return of(Stream.of(new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2), new SimpleImmutableEntry<>(k3, v3), new SimpleImmutableEntry<>(k4, v4), new SimpleImmutableEntry<>(k5, v5), new SimpleImmutableEntry<>(k6, v6), new SimpleImmutableEntry<>(k7, v7), new SimpleImmutableEntry<>(k8, v8), new SimpleImmutableEntry<>(k9, v9))); } /** * Returns a sequential {@code EntryStream} containing ten key-value pairs * * @param <K> the type of key * @param <V> the type of value * @param k1 the key of the first element * @param v1 the value of the first element * @param k2 the key of the second element * @param v2 the value of the second element * @param k3 the key of the third element * @param v3 the value of the third element * @param k4 the key of the fourth element * @param v4 the value of the fourth element * @param k5 the key of the fifth element * @param v5 the value of the fifth element * @param k6 the key of the sixth element * @param v6 the value of the sixth element * @param k7 the key of the seventh element * @param v7 the value of the seventh element * @param k8 the key of the eighth element * @param v8 the value of the eighth element * @param k9 the key of the ninth element * @param v9 the value of the ninth element * @param k10 the key of the tenth element * @param v10 the value of the tenth element * @return a sequential stream * @since 0.5.2 */ public static <K, V> EntryStream<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) { return of(Stream.of(new SimpleImmutableEntry<>(k1, v1), new SimpleImmutableEntry<>(k2, v2), new SimpleImmutableEntry<>(k3, v3), new SimpleImmutableEntry<>(k4, v4), new SimpleImmutableEntry<>(k5, v5), new SimpleImmutableEntry<>(k6, v6), new SimpleImmutableEntry<>(k7, v7), new SimpleImmutableEntry<>(k8, v8), new SimpleImmutableEntry<>(k9, v9), new SimpleImmutableEntry<>(k10, v10))); } /** * Returns a sequential {@code EntryStream} containing {@code Entry} objects * composed from corresponding key and value in given two lists. * * <p> * The keys and values are accessed using {@link List#get(int)}, so the * lists should provide fast random access. The lists are assumed to be * unmodifiable during the stream operations. * * @param <K> the type of stream element keys * @param <V> the type of stream element values * @param keys the list of keys, assumed to be unmodified during use * @param values the list of values, assumed to be unmodified during use * @return a new {@code EntryStream} * @throws IllegalArgumentException if length of the lists differs. * @see StreamEx#zip(List, List, BiFunction) * @since 0.2.1 */ public static <K, V> EntryStream<K, V> zip(List<K> keys, List<V> values) { return of(new RangeBasedSpliterator.ZipRef<>(0, checkLength(keys.size(), values.size()), SimpleImmutableEntry<K, V>::new, keys, values)); } /** * Returns a sequential {@code EntryStream} containing {@code Entry} objects * composed from corresponding key and value in given two arrays. * * @param <K> the type of stream element keys * @param <V> the type of stream element values * @param keys the array of keys * @param values the array of values * @return a new {@code EntryStream} * @throws IllegalArgumentException if length of the arrays differs. * @see StreamEx#zip(Object[], Object[], BiFunction) * @since 0.2.1 */ public static <K, V> EntryStream<K, V> zip(K[] keys, V[] values) { return zip(Arrays.asList(keys), Arrays.asList(values)); } /** * Returns a sequential ordered {@code EntryStream} containing the possible * pairs of elements taken from the provided list. * * <p> * Both keys and values are taken from the input list. The index of the key * is always strictly less than the index of the value. The pairs in the * stream are lexicographically ordered. For example, for the list of three * elements the stream of three elements is created: * {@code Map.Entry(list.get(0), list.get(1))}, * {@code Map.Entry(list.get(0), list.get(2))} and * {@code Map.Entry(list.get(1), list.get(2))}. The number of elements in * the resulting stream is {@code list.size()*(list.size()+1L)/2}. * * <p> * The list values are accessed using {@link List#get(int)}, so the list * should provide fast random access. The list is assumed to be unmodifiable * during the stream operations. * * @param <T> type of the list elements * @param list a list to take the elements from * @return a new {@code EntryStream} * @see StreamEx#ofPairs(List, BiFunction) * @since 0.3.6 */ public static <T> EntryStream<T, T> ofPairs(List<T> list) { return of(new PairPermutationSpliterator<>(list, SimpleImmutableEntry<T, T>::new)); } /** * Returns a sequential ordered {@code EntryStream} containing the possible * pairs of elements taken from the provided array. * * <p> * Both keys and values are taken from the input array. The index of the key * is always strictly less than the index of the value. The pairs in the * stream are lexicographically ordered. For example, for the array of three * elements the stream of three elements is created: * {@code Map.Entry(array[0], array[1])}, * {@code Map.Entry(array[0], array[2])} and * {@code Map.Entry(array[1], array[2])}. The number of elements in the * resulting stream is {@code array.length*(array.length+1L)/2}.. * * @param <T> type of the array elements * @param array a array to take the elements from * @return a new {@code EntryStream} * @see StreamEx#ofPairs(Object[], BiFunction) * @since 0.3.6 */ public static <T> EntryStream<T, T> ofPairs(T[] array) { return ofPairs(Arrays.asList(array)); } /** * Return a new {@link EntryStream} containing all the nodes of tree-like * data structure in entry values along with the corresponding tree depths * in entry keys, in depth-first order. * * <p> * The keys of the returned stream are non-negative integer numbers. 0 is * used for the root node only, 1 is for root immediate children, 2 is for * their children and so on. * * <p> * The streams created by mapper may be automatically * {@link java.util.stream.BaseStream#close() closed} after its contents * already consumed and unnecessary anymore. It's not guaranteed that all * created streams will be closed during the stream terminal operation. If * it's necessary to close all the created streams, call the {@code close()} * method of the resulting stream returned by {@code ofTree()}. * * @param <T> the type of tree nodes * @param root root node of the tree * @param mapper a non-interfering, stateless function to apply to each tree * node and its depth which returns null for leaf nodes or stream of * direct children for non-leaf nodes. * @return the new sequential ordered {@code EntryStream} * @since 0.5.2 * @see StreamEx#ofTree(Object, Function) * @see #ofTree(Object, Class, BiFunction) */ public static <T> EntryStream<Integer, T> ofTree(T root, BiFunction<Integer, T, Stream<T>> mapper) { TreeSpliterator<T, Entry<Integer, T>> spliterator = new TreeSpliterator.Depth<>(root, mapper); return new EntryStream<>(spliterator, StreamContext.SEQUENTIAL.onClose(spliterator::close)); } /** * Return a new {@link EntryStream} containing all the nodes of tree-like * data structure in entry values along with the corresponding tree depths * in entry keys, in depth-first order. * * <p> * The keys of the returned stream are non-negative integer numbers. 0 is * used for the root node only, 1 is for root immediate children, 2 is for * their children and so on. * * <p> * The streams created by mapper may be automatically * {@link java.util.stream.BaseStream#close() closed} after its contents * already consumed and unnecessary anymore. It's not guaranteed that all * created streams will be closed during the stream terminal operation. If * it's necessary to close all the created streams, call the {@code close()} * method of the resulting stream returned by {@code ofTree()}. * * @param <T> the base type of tree nodes * @param <TT> the sub-type of composite tree nodes which may have children * @param root root node of the tree * @param collectionClass a class representing the composite tree node * @param mapper a non-interfering, stateless function to apply to each * composite tree node and its depth which returns stream of direct * children. May return null if the given node has no children. * @return the new sequential ordered stream * @since 0.5.2 * @see StreamEx#ofTree(Object, Class, Function) * @see #ofTree(Object, BiFunction) */ @SuppressWarnings("unchecked") public static <T, TT extends T> EntryStream<Integer, T> ofTree(T root, Class<TT> collectionClass, BiFunction<Integer, TT, Stream<T>> mapper) { return ofTree(root, (d, t) -> collectionClass.isInstance(t) ? mapper.apply(d, (TT) t) : null); } }