All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.util.stream.RemoteCollectors Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */
package com.tangosol.util.stream;

import com.tangosol.internal.util.DoubleSummaryStatistics;
import com.tangosol.internal.util.IntSummaryStatistics;
import com.tangosol.internal.util.LongSummaryStatistics;

import com.tangosol.internal.util.collection.PortableCollection;
import com.tangosol.internal.util.collection.PortableList;
import com.tangosol.internal.util.collection.PortableMap;
import com.tangosol.internal.util.collection.PortableSet;
import com.tangosol.internal.util.collection.PortableSortedSet;

import com.tangosol.internal.util.invoke.Lambdas;

import com.tangosol.internal.util.stream.collectors.AveragingDoubleCollector;
import com.tangosol.internal.util.stream.collectors.AveragingIntCollector;
import com.tangosol.internal.util.stream.collectors.AveragingLongCollector;
import com.tangosol.internal.util.stream.collectors.BiReducingCollector;
import com.tangosol.internal.util.stream.collectors.CollectingAndThenCollector;
import com.tangosol.internal.util.stream.collectors.CollectionCollector;
import com.tangosol.internal.util.stream.collectors.GroupingByCollector;
import com.tangosol.internal.util.stream.collectors.MapCollector;
import com.tangosol.internal.util.stream.collectors.MappingCollector;
import com.tangosol.internal.util.stream.collectors.ReducingCollector;
import com.tangosol.internal.util.stream.collectors.SummarizingDoubleCollector;
import com.tangosol.internal.util.stream.collectors.SummarizingIntCollector;
import com.tangosol.internal.util.stream.collectors.SummarizingLongCollector;
import com.tangosol.internal.util.stream.collectors.SummingDoubleCollector;
import com.tangosol.internal.util.stream.collectors.SummingIntCollector;
import com.tangosol.internal.util.stream.collectors.SummingLongCollector;

import com.tangosol.util.InvocableMap;
import com.tangosol.util.SimpleHolder;
import com.tangosol.util.SortedBag;
import com.tangosol.util.ValueExtractor;

import com.tangosol.util.comparator.SafeComparator;

import com.tangosol.util.function.Remote;

import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;

import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Function;

/**
 * Static factory for various {@link RemoteCollector}s that can be executed in
 * parallel in a distributed environment.
 *
 * @author as  2014.10.01
 * @since 12.2.1
 *
 * @see RemoteCollector
 * @see java.util.stream.Collectors
 */
public abstract class RemoteCollectors
    {
    /**
     * Returns a {@code Collector} that accumulates the input elements into a
     * new {@code Collection}, in encounter order.  The {@code Collection} is
     * created by the provided factory.
     *
     * @param        the type of the input elements
     * @param        the type of the resulting {@code Collection}
     * @param supplier  a {@code Supplier} which returns a new, empty
     *                  {@code Collection} of the appropriate type
     *
     * @return a {@code Collector} which collects all the input elements into a
     * {@code Collection}, in encounter order
     */
    public static > RemoteCollector toCollection(Remote.Supplier supplier)
        {
        return new CollectionCollector<>(supplier);
        }

    /**
     * Returns a {@code Collector} that accumulates the input elements into a
     * new {@code List}. There are no guarantees on the type, mutability,
     * serializability, or thread-safety of the {@code List} returned; if more
     * control over the returned {@code List} is required, use {@link
     * #toCollection(Remote.Supplier)}.
     *
     * @param  the type of the input elements
     *
     * @return a {@code Collector} which collects all the input elements into a
     * {@code List}, in encounter order
     */
    public static  RemoteCollector> toList()
        {
        return toCollection(PortableList::new);
        }

    /**
     * Returns a {@code Collector} that accumulates the input elements into a
     * new {@code SortedBag}.
     *
     * @param  the type of the input elements
     *
     * @return a {@code Collector} which collects all the input elements into a
     * {@code List}, in encounter order
     */
    public static  RemoteCollector> toSortedBag()
        {
        return toSortedBag(SafeComparator.INSTANCE);
        }

    /**
     * Returns a {@code Collector} that accumulates the input elements into a
     * new {@code SortedBag}.
     *
     * @param          the type of the input elements
     * @param comparator  a comparator for type T
     *
     * @return a {@code Collector} which collects all the input elements into a
     * {@code List}, in encounter order
     */
    public static  RemoteCollector> toSortedBag(Comparator comparator)
        {
        Remote.Supplier> supplier = () -> new SortedBag<>(comparator);
        return toCollection(() -> new PortableCollection<>(supplier));
        }

    /**
     * Returns a {@code Collector} that accumulates the input elements into a
     * new {@code SortedBag}.
     *
     * @param  the type of the input elements
     * @param comparator  a comparator for type T
     *
     * @return a {@code Collector} which collects all the input elements into a
     * {@code List}, in encounter order
     */
    public static  RemoteCollector> toSortedBag(Remote.Comparator comparator)
        {
        Remote.Supplier> supplier = () -> new SortedBag<>(comparator);
        return toCollection(() -> new PortableCollection<>(supplier));
        }

    /**
     * Returns a {@code Collector} that accumulates the input elements into a
     * new {@code Set}. There are no guarantees on the type, mutability,
     * serializability, or thread-safety of the {@code Set} returned; if more
     * control over the returned {@code Set} is required, use {@link
     * #toCollection(Remote.Supplier)}.
     * 

* This is an {@link RemoteCollector.Characteristics#UNORDERED unordered} * Collector. * * @param the type of the input elements * * @return a {@code Collector} which collects all the input elements into a * {@code Set} */ public static RemoteCollector> toSet() { return toCollection(PortableSet::new); } /** * Returns a {@code Collector} that accumulates the input elements into a * new {@code SortedSet}. There are no guarantees on the type, mutability, * serializability, or thread-safety of the {@code SortedSet} returned; * if more control over the returned {@code SortedSet} is required, use * {@link #toCollection(Remote.Supplier)}. *

* This is an {@link RemoteCollector.Characteristics#UNORDERED unordered} * Collector. * * @param the type of the input elements * * @return a {@code Collector} which collects all the input elements into a * {@code Set} */ public static RemoteCollector> toSortedSet() { return toCollection(PortableSortedSet::new); } /** * Returns a {@code Collector} that accumulates the input elements into a * new {@code SortedSet}. There are no guarantees on the type, mutability, * serializability, or thread-safety of the {@code SortedSet} returned; * if more control over the returned {@code SortedSet} is required, use * {@link #toCollection(Remote.Supplier)}. *

* This is an {@link RemoteCollector.Characteristics#UNORDERED unordered} * Collector. * * @param the type of the input elements * @param comparator a comparator for type T * * @return a {@code Collector} which collects all the input elements into a * {@code Set} */ public static RemoteCollector> toSortedSet(Comparator comparator) { return toCollection(() -> new PortableSortedSet<>(comparator)); } /** * Returns a {@code Collector} that accumulates the input elements into a * new {@code SortedSet}. There are no guarantees on the type, mutability, * serializability, or thread-safety of the {@code SortedSet} returned; * if more control over the returned {@code SortedSet} is required, use * {@link #toCollection(Remote.Supplier)}. *

* This is an {@link RemoteCollector.Characteristics#UNORDERED unordered} * Collector. * * @param the type of the input elements * @param comparator a comparator for type T * * @return a {@code Collector} which collects all the input elements into a * {@code Set} */ public static RemoteCollector> toSortedSet(Remote.Comparator comparator) { return toCollection(() -> new PortableSortedSet<>(comparator)); } /** * Adapts a {@code Collector} accepting elements of type {@code U} to one * accepting elements of type {@code T} by applying a mapping function to * each input element before accumulation. * * @param the type of the input elements * @param type of elements accepted by downstream collector * @param intermediate accumulation type of the downstream * collector * @param result type of collector * @param mapper a function to be applied to the input elements * @param downstream a collector which will accept mapped values * * @return a collector which applies the mapping function to the input * elements and provides the mapped results to the downstream collector * * @apiNote The {@code mapping()} collectors are most useful when used in a * multi-level reduction, such as downstream of a {@code groupingBy} or * {@code partitioningBy}. For example, given a stream of {@code Person}, * to accumulate the set of last names in each city: *

{@code
     *     Map> lastNamesByCity
     *         = people.stream().collect(groupingBy(Person::getCity,
     *                                              mapping(Person::getLastName,
     * toSet())));
     * }
*/ public static RemoteCollector mapping(Remote.Function mapper, RemoteCollector downstream) { return new MappingCollector(mapper, downstream); } /** * Adapts a {@code Collector} to perform an additional finishing * transformation. For example, one could adapt the {@link #toList()} * collector to always produce an immutable list with: *
{@code
     *     List people
     *         = people.stream().collect(collectingAndThen(toList(),
     * Collections::unmodifiableList));
     * }
* * @param the type of the input elements * @param
intermediate accumulation type of the downstream * collector * @param result type of the downstream collector * @param result type of the resulting collector * @param downstream a collector * @param finisher a function to be applied to the final result of the * downstream collector * * @return a collector which performs the action of the downstream * collector, followed by an additional finishing step */ public static RemoteCollector collectingAndThen(RemoteCollector downstream, Remote.Function finisher) { return new CollectingAndThenCollector<>(downstream, finisher); } /** * Returns a {@code Collector} accepting elements of type {@code T} that * counts the number of input elements. If no elements are present, the * result is 0. * * @param the type of the input elements * * @return a {@code Collector} that counts the input elements * * @implSpec This produces a result equivalent to: *
{@code
     *     reducing(0L, e -> 1L, Long::sum)
     * }
*/ public static RemoteCollector counting() { return reducing(0L, e -> 1L, Long::sum); } /** * Returns a {@code Collector} that produces the minimal element according * to a given {@code Comparator}, described as an {@code Optional}. * * @param the type of the input elements * @param comparator a {@code Comparator} for comparing elements * * @return a {@code Collector} that produces the minimal value * * @implSpec This produces a result equivalent to: *
{@code
     *     reducing(Remote.BinaryOperator.minBy(comparator))
     * }
*/ public static RemoteCollector> minBy(Remote.Comparator comparator) { return reducing(Remote.BinaryOperator.minBy(comparator)); } /** * Returns a {@code Collector} that produces the maximal element according * to a given {@code Comparator}, described as an {@code Optional}. * * @param the type of the input elements * @param comparator a {@code Comparator} for comparing elements * * @return a {@code Collector} that produces the maximal value * * @implSpec This produces a result equivalent to: *
{@code
     *     reducing(BinaryOperator.maxBy(comparator))
     * }
*/ public static RemoteCollector> maxBy(Remote.Comparator comparator) { return reducing(Remote.BinaryOperator.maxBy(comparator)); } /** * Returns a {@code Collector} that produces the sum of a integer-valued * function applied to the input elements. If no elements are present, the * result is 0. * * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param extractor a function extracting the property to be summed * * @return a {@code Collector} that produces the sum of a derived property */ public static RemoteCollector summingInt(ValueExtractor extractor) { ValueExtractor ex = Lambdas.ensureRemotable(extractor); Remote.ToIntFunction mapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(ex).intValue() : ex.extract((U) t).intValue(); return new SummingIntCollector<>(mapper); } /** * Returns a {@code Collector} that produces the sum of a long-valued * function applied to the input elements. If no elements are present, the * result is 0. * * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param extractor a function extracting the property to be summed * * @return a {@code Collector} that produces the sum of a derived property */ public static RemoteCollector summingLong(ValueExtractor extractor) { ValueExtractor ex = Lambdas.ensureRemotable(extractor); Remote.ToLongFunction mapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(ex).longValue() : ex.extract((U) t).longValue(); return new SummingLongCollector<>(mapper); } /** * Returns a {@code Collector} that produces the sum of a double-valued * function applied to the input elements. If no elements are present, the * result is 0. *

* The sum returned can vary depending upon the order in which values are * recorded, due to accumulated rounding error in addition of values of * differing magnitudes. Values sorted by increasing absolute magnitude tend * to yield more accurate results. If any recorded value is a {@code NaN} * or the sum is at any point a {@code NaN} then the sum will be {@code * NaN}. * * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param extractor a function extracting the property to be summed * * @return a {@code Collector} that produces the sum of a derived property */ public static RemoteCollector summingDouble(ValueExtractor extractor) { ValueExtractor ex = Lambdas.ensureRemotable(extractor); Remote.ToDoubleFunction mapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(ex).doubleValue() : ex.extract((U) t).doubleValue(); return new SummingDoubleCollector<>(mapper); } /** * Returns a {@code Collector} that produces the arithmetic mean of an * integer-valued function applied to the input elements. If no elements * are present, the result is 0. * * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param extractor a function extracting the property to be summed * * @return a {@code Collector} that produces the sum of a derived property */ public static RemoteCollector averagingInt(ValueExtractor extractor) { ValueExtractor ex = Lambdas.ensureRemotable(extractor); Remote.ToIntFunction mapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(ex).intValue() : ex.extract((U) t).intValue(); return new AveragingIntCollector<>(mapper); } /** * Returns a {@code Collector} that produces the arithmetic mean of a * long-valued function applied to the input elements. If no elements are * present, the result is 0. * * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param extractor a function extracting the property to be averaged * * @return a {@code Collector} that produces the average of a derived property */ public static RemoteCollector averagingLong(ValueExtractor extractor) { ValueExtractor ex = Lambdas.ensureRemotable(extractor); Remote.ToLongFunction mapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(ex).longValue() : ex.extract((U) t).longValue(); return new AveragingLongCollector<>(mapper); } /** * Returns a {@code Collector} that produces the arithmetic mean of a * double-valued function applied to the input elements. If no elements are * present, the result is 0. *

* The average returned can vary depending upon the order in which values * are recorded, due to accumulated rounding error in addition of values of * differing magnitudes. Values sorted by increasing absolute magnitude tend * to yield more accurate results. If any recorded value is a {@code NaN} * or the sum is at any point a {@code NaN} then the average will be {@code * NaN}. * * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param extractor a function extracting the property to be summed * * @return a {@code Collector} that produces the sum of a derived property * * @implNote The {@code double} format can represent all consecutive * integers in the range -253 to 253. If the pipeline * has more than 253 values, the divisor in the average * computation will saturate at 253, leading to additional * numerical errors. */ public static RemoteCollector averagingDouble(ValueExtractor extractor) { ValueExtractor ex = Lambdas.ensureRemotable(extractor); Remote.ToDoubleFunction mapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(ex).doubleValue() : ex.extract((U) t).doubleValue(); return new AveragingDoubleCollector<>(mapper); } /** * Returns a {@code Collector} which performs a reduction of its input * elements under a specified {@code BinaryOperator} using the provided * identity. * * @param element type for the input and output of the reduction * @param identity the identity value for the reduction (also, the value * that is returned when there are no input elements) * @param op a {@code BinaryOperator} used to reduce the input * elements * * @return a {@code Collector} which implements the reduction operation * * @apiNote The {@code reducing()} collectors are most useful when used in a * multi-level reduction, downstream of {@code groupingBy} or {@code * partitioningBy}. To perform a simple reduction on a stream, use {@link * RemoteStream#reduce(Object, BinaryOperator)}} instead. * @see #reducing(Remote.BinaryOperator) * @see #reducing(Object, Remote.Function, Remote.BinaryOperator) */ public static RemoteCollector, T> reducing(T identity, Remote.BinaryOperator op) { return new ReducingCollector<>(identity, op); } /** * Returns a {@code Collector} which performs a reduction of its input * elements under a specified {@code BinaryOperator}. The result is * described as an {@code Optional}. * * @param element type for the input and output of the reduction * @param op a {@code BinaryOperator} used to reduce the input elements * * @return a {@code Collector} which implements the reduction operation * * @apiNote The {@code reducing()} collectors are most useful when used in a * multi-level reduction, downstream of {@code groupingBy} or {@code * partitioningBy}. To perform a simple reduction on a stream, use {@link * RemoteStream#reduce(BinaryOperator)} instead. *

* For example, given a stream of {@code Person}, to calculate tallest * person in each city: *

{@code
     *     Comparator byHeight = Comparator.comparing(Person::getHeight);
     *     Map tallestByCity
     *         = people.stream().collect(groupingBy(Person::getCity,
     * reducing(BinaryOperator.maxBy(byHeight))));
     * }
* @see #reducing(Object, Remote.BinaryOperator) * @see #reducing(Object, Remote.Function, Remote.BinaryOperator) */ public static RemoteCollector> reducing(Remote.BinaryOperator op) { return collectingAndThen(reducing(null, op), Optional::ofNullable); } /** * Returns a {@code Collector} which performs a reduction of its input * elements under a specified mapping function and {@code BinaryOperator}. * This is a generalization of {@link #reducing(Object, Remote.BinaryOperator)} * which allows a transformation of the elements before reduction. * * @param the type of the input elements * @param the type of the mapped values * @param identity the identity value for the reduction (also, the value * that is returned when there are no input elements) * @param mapper a mapping function to apply to each input value * @param op a {@code BinaryOperator} used to reduce the mapped * values * * @return a {@code Collector} implementing the map-reduce operation * * @apiNote The {@code reducing()} collectors are most useful when used in a * multi-level reduction, downstream of {@code groupingBy} or {@code * partitioningBy}. To perform a simple map-reduce on a stream, use {@link * RemoteStream#map(Function)} and {@link RemoteStream#reduce(Object, BinaryOperator)} * instead. *

* For example, given a stream of {@code Person}, to calculate the * longest last name of residents in each city: *

{@code
     *     Comparator byLength = Comparator.comparing(String::length);
     *     Map longestLastNameByCity
     *         = people.stream().collect(groupingBy(Person::getCity,
     *                                              reducing(Person::getLastName,
     * BinaryOperator.maxBy(byLength))));
     * }
* @see #reducing(Object, Remote.BinaryOperator) * @see #reducing(Remote.BinaryOperator) */ public static RemoteCollector reducing(U identity, Remote.Function mapper, Remote.BinaryOperator op) { return mapping(mapper, reducing(identity, op)); } /** * Returns a {@code Collector} which performs a reduction of its input * elements under a specified mapping function and {@code BinaryOperator}. * This is a generalization of {@link #reducing(Object, Remote.BinaryOperator)} * which allows a transformation of the elements before reduction. * * @param the type of the input elements * @param the type of the mapped values * @param identity the identity value for the reduction (also, the value * that is returned when there are no input elements) * @param mapper a mapping function to apply to each input value * @param op a {@code BinaryOperator} used to reduce the mapped * values * * @return a {@code Collector} implementing the map-reduce operation * * @apiNote The {@code reducing()} collectors are most useful when used in a * multi-level reduction, downstream of {@code groupingBy} or {@code * partitioningBy}. To perform a simple map-reduce on a stream, use {@link * RemoteStream#map(Function)} and {@link RemoteStream#reduce(Object, BinaryOperator)} * instead. *

* For example, given a stream of {@code Person}, to calculate the * longest last name of residents in each city: *

{@code
     *     Comparator byLength = Comparator.comparing(String::length);
     *     Map longestLastNameByCity
     *         = people.stream().collect(groupingBy(Person::getCity,
     *                                              reducing(Person::getLastName,
     * BinaryOperator.maxBy(byLength))));
     * }
* @see #reducing(Object, Remote.BinaryOperator) * @see #reducing(Remote.BinaryOperator) */ public static RemoteCollector reducing(U identity, Remote.BiFunction mapper, Remote.BinaryOperator op) { return new BiReducingCollector<>(identity, mapper, op); } /** * Returns a {@code Collector} implementing a "group by" operation on input * elements of type {@code T}, grouping elements according to a * classification function, and returning the results in a {@code Map}. *

* The classification function maps elements to some key type {@code K}. * The collector produces a {@code Map>} whose keys are the * values resulting from applying the classification function to the input * elements, and whose corresponding values are {@code List}s containing the * input elements which map to the associated key under the classification * function. *

* There are no guarantees on the type, mutability, serializability, or * thread-safety of the {@code Map} or {@code List} objects returned. * * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param the type of the keys * @param classifier the classifier function mapping input elements to keys * * @return a {@code Collector} implementing the group-by operation * * @implSpec This produces a result similar to: *

{@code
     *     groupingBy(classifier, toList());
     * }
* @see #groupingBy(ValueExtractor, RemoteCollector) * @see #groupingBy(ValueExtractor, Remote.Supplier, RemoteCollector) */ public static RemoteCollector>> groupingBy(ValueExtractor classifier) { return groupingBy(classifier, toList()); } /** * Returns a {@code Collector} implementing a cascaded "group by" operation * on input elements of type {@code T}, grouping elements according to a * classification function, and then performing a reduction operation on the * values associated with a given key using the specified downstream {@code * Collector}. *

* The classification function maps elements to some key type {@code K}. * The downstream collector operates on elements of type {@code T} and * produces a result of type {@code D}. The resulting collector produces a * {@code Map}. *

* There are no guarantees on the type, mutability, serializability, or * thread-safety of the {@code Map} returned. *

* For example, to compute the set of last names of people in each city: *

{@code
     *     Map> namesByCity
     *         = people.stream().collect(groupingBy(Person::getCity,
     *                                              mapping(Person::getLastName,
     * toSet())));
     * }
* * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param the type of the keys * @param
the intermediate accumulation type of the downstream * collector * @param the result type of the downstream reduction * @param classifier a classifier function mapping input elements to keys * @param downstream a {@code Collector} implementing the downstream * reduction * * @return a {@code Collector} implementing the cascaded group-by operation * * @see #groupingBy(ValueExtractor) * @see #groupingBy(ValueExtractor, Remote.Supplier, RemoteCollector) */ public static RemoteCollector> groupingBy(ValueExtractor classifier, RemoteCollector downstream) { return groupingBy(classifier, HashMap::new, downstream); } /** * Returns a {@code Collector} implementing a cascaded "group by" operation * on input elements of type {@code T}, grouping elements according to a * classification function, and then performing a reduction operation on the * values associated with a given key using the specified downstream {@code * Collector}. The {@code Map} produced by the Collector is created with * the supplied factory function. *

* The classification function maps elements to some key type {@code K}. * The downstream collector operates on elements of type {@code T} and * produces a result of type {@code D}. The resulting collector produces a * {@code Map}. *

* For example, to compute the set of last names of people in each city, * where the city names are sorted: *

{@code
     *     Map> namesByCity
     *         = people.stream().collect(groupingBy(Person::getCity,
     * TreeMap::new,
     *                                              mapping(Person::getLastName,
     * toSet())));
     * }
* * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param the type of the keys * @param
the intermediate accumulation type of the downstream * collector * @param the result type of the downstream reduction * @param the type of the resulting {@code Map} * @param extractor an extractor function mapping input elements to keys * @param downstream a {@code Collector} implementing the downstream * reduction * @param mapFactory a function which, when called, produces a new empty * {@code Map} of the desired type * * @return a {@code Collector} implementing the cascaded group-by operation * * @see #groupingBy(ValueExtractor, RemoteCollector) * @see #groupingBy(ValueExtractor) */ public static > RemoteCollector groupingBy(ValueExtractor extractor, Remote.Supplier mapFactory, RemoteCollector downstream) { ValueExtractor ex = Lambdas.ensureRemotable(extractor); Remote.Function classifier = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(ex) : ex.extract((U) t); return new GroupingByCollector<>(classifier, downstream, mapFactory); } /** * Returns a {@code Collector} that accumulates elements into a {@code Map} * whose keys and values are the result of applying the provided mapping * functions to the input elements. *

* If the mapped keys contains duplicates (according to {@link * Object#equals(Object)}), an {@code IllegalStateException} is thrown when * the collection operation is performed. If the mapped keys may have * duplicates, use {@link #toMap(ValueExtractor, ValueExtractor, Remote.BinaryOperator)} * instead. * * @param the type of the stream elements * @param the type of the objects to extract keys from, which * should be either the same as {@code T}, or the key or * value type if the {@code T} is {@code InvocableMap.Entry} * @param the type of the objects to extract values from, which * should be either the same as {@code T}, or the key or * value type if the {@code T} is {@code InvocableMap.Entry} * @param the output type of the key mapping function * @param the output type of the value mapping function * @param keyMapper a mapping function to produce keys * @param valueMapper a mapping function to produce values * * @return a {@code Collector} which collects elements into a {@code Map} * whose keys and values are the result of applying mapping functions to the * input elements * * @apiNote It is common for either the key or the value to be the input * elements. In this case, the utility method {@link * java.util.function.Function#identity()} may be helpful. For example, the * following produces a {@code Map} mapping students to their grade point * average: *

{@code
     *     Map studentToGPA
     *         students.stream().collect(toMap(Functions.identity(),
     *                                         student ->
     * computeGPA(student)));
     * }
* And the following produces a {@code Map} mapping a unique identifier to * students: *
{@code
     *     Map studentIdToStudent
     *         students.stream().collect(toMap(Student::getId,
     *                                         Functions.identity());
     * }
* @see #toMap(ValueExtractor, ValueExtractor, Remote.BinaryOperator) * @see #toMap(ValueExtractor, ValueExtractor, Remote.BinaryOperator, Remote.Supplier) */ public static RemoteCollector> toMap(ValueExtractor keyMapper, ValueExtractor valueMapper) { return toMap(keyMapper, valueMapper, null, PortableMap::new); } /** * Returns a {@code Collector} that accumulates elements into a {@code Map} * whose keys and values are the result of applying the provided mapping * functions to the input elements. *

* 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. * * @param the type of the stream elements * @param the type of the objects to extract keys from, which * should be either the same as {@code T}, or the key or * value type if the {@code T} is {@code InvocableMap.Entry} * @param the type of the objects to extract values from, which * should be either the same as {@code T}, or the key or * value type if the {@code T} is {@code InvocableMap.Entry} * @param the output type of the key mapping function * @param the output type of the value mapping function * @param keyMapper a mapping function to produce keys * @param valueMapper a mapping function to produce values * @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 Collector} which collects elements into a {@code Map} * whose keys are the result of applying a key mapping function to the input * elements, and whose values are the result of applying a value mapping * function to all input elements equal to the key and combining them using * the merge function * * @apiNote There are multiple ways to deal with collisions between multiple * elements mapping to the same key. The other forms of {@code toMap} * simply use a merge function that throws unconditionally, but you can * easily write more flexible merge policies. For example, if you have a * stream of {@code Person}, and you want to produce a "phone book" mapping * name to address, but it is possible that two persons have the same name, * you can do as follows to gracefully deals with these collisions, and * produce a {@code Map} mapping names to a concatenated list of addresses: *

{@code
     *     Map phoneBook
     *         people.stream().collect(toMap(Person::getName,
     *                                       Person::getAddress,
     *                                       (s, a) -> s + ", " + a));
     * }
* * @see #toMap(ValueExtractor, ValueExtractor) * @see #toMap(ValueExtractor, ValueExtractor, Remote.BinaryOperator, Remote.Supplier) */ public static RemoteCollector> toMap( ValueExtractor keyMapper, ValueExtractor valueMapper, Remote.BinaryOperator mergeFunction) { return toMap(keyMapper, valueMapper, mergeFunction, PortableMap::new); } /** * Returns a {@code Collector} that accumulates elements into a {@code Map} * whose keys and values are the result of applying the provided mapping * functions to the input elements. *

* 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. The {@code Map} is created by a provided supplier function. * * @param the type of the stream elements * @param the type of the objects to extract keys from, which * should be either the same as {@code T}, or the key or * value type if the {@code T} is {@code InvocableMap.Entry} * @param the type of the objects to extract values from, which * should be either the same as {@code T}, or the key or * value type if the {@code T} is {@code InvocableMap.Entry} * @param the output type of the key mapping function * @param the output type of the value mapping function * @param the type of the resulting {@code Map} * @param keyExtractor a mapping function to produce keys * @param valueExtractor a mapping function to produce values * @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)} * @param mapSupplier a function which returns a new, empty {@code Map} * into which the results will be inserted * * @return a {@code Collector} which collects elements into a {@code Map} * whose keys are the result of applying a key mapping function to the input * elements, and whose values are the result of applying a value mapping * function to all input elements equal to the key and combining them using * the merge function * * @see #toMap(ValueExtractor, ValueExtractor) * @see #toMap(ValueExtractor, ValueExtractor, Remote.BinaryOperator) */ public static > RemoteCollector toMap( ValueExtractor keyExtractor, ValueExtractor valueExtractor, Remote.BinaryOperator mergeFunction, Remote.Supplier mapSupplier) { ValueExtractor keyEx = Lambdas.ensureRemotable(keyExtractor); ValueExtractor valueEx = Lambdas.ensureRemotable(valueExtractor); Remote.Function keyMapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(keyEx) : keyEx.extract((U1) t); Remote.Function valueMapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(valueEx) : valueEx.extract((U2) t); return new MapCollector<>(keyMapper, valueMapper, mergeFunction, mapSupplier); } /** * Returns a {@code Collector} which applies an {@code int}-producing * mapping function to each input element, and returns summary statistics * for the resulting values. * * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param extractor a mapping function to apply to each element * * @return a {@code Collector} implementing the summary-statistics reduction * * @see #summarizingDouble(ValueExtractor) * @see #summarizingLong(ValueExtractor) */ public static RemoteCollector summarizingInt(ValueExtractor extractor) { ValueExtractor ex = Lambdas.ensureRemotable(extractor); Remote.ToIntFunction mapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(ex).intValue() : ex.extract((U) t).intValue(); return new SummarizingIntCollector<>(mapper); } /** * Returns a {@code Collector} which applies an {@code long}-producing * mapping function to each input element, and returns summary statistics * for the resulting values. * * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param extractor a mapping function to apply to each element * * @return a {@code Collector} implementing the summary-statistics reduction * * @see #summarizingDouble(ValueExtractor) * @see #summarizingInt(ValueExtractor) */ public static RemoteCollector summarizingLong(ValueExtractor extractor) { ValueExtractor ex = Lambdas.ensureRemotable(extractor); Remote.ToLongFunction mapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(ex).longValue() : ex.extract((U) t).longValue(); return new SummarizingLongCollector<>(mapper); } /** * Returns a {@code Collector} which applies an {@code double}-producing * mapping function to each input element, and returns summary statistics * for the resulting values. * * @param the type of the stream elements * @param the type of the objects to extract from, which should be * either the same as {@code T}, or the key or value type * if the {@code T} is {@code InvocableMap.Entry} * @param extractor a mapping function to apply to each element * * @return a {@code Collector} implementing the summary-statistics reduction * * @see #summarizingLong(ValueExtractor) * @see #summarizingInt(ValueExtractor) */ public static RemoteCollector summarizingDouble(ValueExtractor extractor) { ValueExtractor ex = Lambdas.ensureRemotable(extractor); Remote.ToDoubleFunction mapper = t -> t instanceof InvocableMap.Entry ? ((InvocableMap.Entry) t).extract(ex).doubleValue() : ex.extract((U) t).doubleValue(); return new SummarizingDoubleCollector<>(mapper); } }