Java 8 个用于 Guava 不可变集合的收集器?
Java 8 collector for Guava immutable collections?
我非常喜欢Java 8 streams 和Guava 的不可变集合,但我不知道如何将两者结合使用。
例如,我如何实现一个 Java 8 Collector that gathers stream results into an ImmutableMultimap?
奖励积分:我希望能够提供 key/value 映射器,类似于 Collectors.toMap() 的工作方式。
更新:我在 https://github.com/yanaga/guava-stream and attempted to improve upon it in my own library at https://bitbucket.org/cowwoc/guava-jdk8/
找到了一个似乎涵盖所有 Guava 集合的实现
由于历史原因,我将在下面保留之前的答案。
神圣的#@!(我明白了!
此实现适用于任何 Multimap(可变或不可变),而 侧重于不可变实现。也就是说,后者对于不可变的情况可能更有效(我不使用构建器)。
import com.google.common.collect.Multimap;
import java.util.EnumSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import org.bitbucket.cowwoc.preconditions.Preconditions;
/**
* A Stream collector that returns a Multimap.
* <p>
* @author Gili Tzabari
* @param <T> the type of the input elements
* @param <K> the type of keys stored in the map
* @param <V> the type of values stored in the map
* @param <R> the output type of the collector
*/
public final class MultimapCollector<T, K, V, R extends Multimap<K, V>>
implements Collector<T, Multimap<K, V>, R>
{
private final Supplier<Multimap<K, V>> mapSupplier;
private final Function<? super T, ? extends K> keyMapper;
private final Function<? super T, ? extends V> valueMapper;
private final Function<Multimap<K, V>, R> resultMapper;
/**
* Creates a new MultimapCollector.
* <p>
* @param mapSupplier a function which returns a new, empty {@code Multimap} into which intermediate results will be
* inserted
* @param keyMapper a function that transforms the map keys
* @param valueMapper a function that transforms the map values
* @param resultMapper a function that transforms the intermediate {@code Multimap} into the final result
* @throws NullPointerException if any of the arguments are null
*/
public MultimapCollector(Supplier<Multimap<K, V>> mapSupplier,
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper,
Function<Multimap<K, V>, R> resultMapper)
{
Preconditions.requireThat(mapSupplier, "mapSupplier").isNotNull();
Preconditions.requireThat(keyMapper, "keyMapper").isNotNull();
Preconditions.requireThat(valueMapper, "valueMapper").isNotNull();
Preconditions.requireThat(resultMapper, "resultMapper").isNotNull();
this.mapSupplier = mapSupplier;
this.keyMapper = keyMapper;
this.valueMapper = valueMapper;
this.resultMapper = resultMapper;
}
@Override
public Supplier<Multimap<K, V>> supplier()
{
return mapSupplier;
}
@Override
public BiConsumer<Multimap<K, V>, T> accumulator()
{
return (map, entry) ->
{
K key = keyMapper.apply(entry);
if (key == null)
throw new IllegalArgumentException("keyMapper(" + entry + ") returned null");
V value = valueMapper.apply(entry);
if (value == null)
throw new IllegalArgumentException("keyMapper(" + entry + ") returned null");
map.put(key, value);
};
}
@Override
public BinaryOperator<Multimap<K, V>> combiner()
{
return (left, right) ->
{
left.putAll(right);
return left;
};
}
@Override
public Function<Multimap<K, V>, R> finisher()
{
return resultMapper;
}
@Override
public Set<Characteristics> characteristics()
{
return EnumSet.noneOf(Characteristics.class);
}
}
[...]
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
/**
* Stream collectors for Guava collections.
* <p>
* @author Gili Tzabari
*/
public final class GuavaCollectors
{
/**
* Returns a {@code Collector} that accumulates elements into a {@code Multimap}.
* <p>
* @param <T> the type of the input elements
* @param <K> the type of the map keys
* @param <V> the type of the map values
* @param <R> the output type of the collector
* @param mapSupplier a function which returns a new, empty {@code Multimap} into which intermediate results will be
* inserted
* @param keyMapper a function that transforms the map keys
* @param valueMapper a function that transforms the map values
* @param resultMapper a function that transforms the intermediate {@code Multimap} into the final result
* @return a {@code Collector} which collects elements into a {@code Multimap} whose keys and values are the result of
* applying mapping functions to the input elements
*/
public static <T, K, V, R extends Multimap<K, V>> Collector<T, ?, R> toMultimap(
Supplier<Multimap<K, V>> mapSupplier,
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper,
Function<Multimap<K, V>, R> resultMapper)
{
return new MultimapCollector<>(mapSupplier, keyMapper, valueMapper, resultMapper);
}
public static void main(String[] args)
{
Multimap<Integer, Double> input = HashMultimap.create();
input.put(10, 20.0);
input.put(10, 25.0);
input.put(50, 60.0);
System.out.println("input: " + input);
ImmutableMultimap<Integer, Double> output = input.entries().stream().collect(
GuavaCollectors.toMultimap(HashMultimap::create,
entry -> entry.getKey() + 1, entry -> entry.getValue() - 1,
ImmutableMultimap::copyOf));
System.out.println("output: " + output);
}
}
main() 输出:
input: {10=[20.0, 25.0], 50=[60.0]}
output: {51=[59.0], 11=[24.0, 19.0]}
资源
- Arjit provided an excellent resource demonstrating how to implement Collectors for other Guava collections: http://blog.comsysto.com/2014/11/12/java-8-collectors-for-guava-collections/
这是一个支持多个 ImmutableMultimap
实现的版本。请注意,第一个方法是私有的,因为它需要不安全的转换。
@SuppressWarnings("unchecked")
private static <T, K, V, M extends ImmutableMultimap<K, V>> Collector<T, ?, M> toImmutableMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction,
Supplier<? extends ImmutableMultimap.Builder<K, V>> builderSupplier) {
return Collector.of(
builderSupplier,
(builder, element) -> builder.put(keyFunction.apply(element), valueFunction.apply(element)),
(left, right) -> {
left.putAll(right.build());
return left;
},
builder -> (M)builder.build());
}
public static <T, K, V> Collector<T, ?, ImmutableMultimap<K, V>> toImmutableMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
return toImmutableMultimap(keyFunction, valueFunction, ImmutableMultimap::builder);
}
public static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
return toImmutableMultimap(keyFunction, valueFunction, ImmutableListMultimap::builder);
}
public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
return toImmutableMultimap(keyFunction, valueFunction, ImmutableSetMultimap::builder);
}
虽然它没有具体回答这个问题,但值得注意的是,这个简单的模式可以帮助每个人从流构建 Guava 的不可变集合,而不需要 Collector
s(因为它们的实现相当困难制作)。
Stream<T> stream = ...
ImmutableXxx<T> collection = ImmutableXxx.copyOf(stream.iterator());
从版本 21 开始,您可以
.collect(ImmutableSet.toImmutableSet())
.collect(ImmutableMap.toImmutableMap())
.collect(Maps.toImmutableEnumMap())
.collect(Sets.toImmutableEnumSet())
.collect(Tables.toTable())
.collect(ImmutableList.toImmutableList())
.collect(Multimaps.toMultimap(...))
对于您的示例,您可以使用 Guava 的 toImmutableMap() 收集器。例如
import static com.google.common.collect.ImmutableMap.toImmutableMap;
ImmutableMap<String, ZipCode> zipCodeForName =
people.stream()
.collect(
toImmutableMap(Person::getName, p -> p.getAddress().getZipCode()));
ImmutableMap<String, Person> personForName =
people.stream()
.collect(
toImmutableMap(Person::getName, p -> p));
具有其余的 Guava 不可变收集器 API
我非常喜欢Java 8 streams 和Guava 的不可变集合,但我不知道如何将两者结合使用。
例如,我如何实现一个 Java 8 Collector that gathers stream results into an ImmutableMultimap?
奖励积分:我希望能够提供 key/value 映射器,类似于 Collectors.toMap() 的工作方式。
更新:我在 https://github.com/yanaga/guava-stream and attempted to improve upon it in my own library at https://bitbucket.org/cowwoc/guava-jdk8/
找到了一个似乎涵盖所有 Guava 集合的实现由于历史原因,我将在下面保留之前的答案。
神圣的#@!(我明白了!
此实现适用于任何 Multimap(可变或不可变),而
import com.google.common.collect.Multimap;
import java.util.EnumSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import org.bitbucket.cowwoc.preconditions.Preconditions;
/**
* A Stream collector that returns a Multimap.
* <p>
* @author Gili Tzabari
* @param <T> the type of the input elements
* @param <K> the type of keys stored in the map
* @param <V> the type of values stored in the map
* @param <R> the output type of the collector
*/
public final class MultimapCollector<T, K, V, R extends Multimap<K, V>>
implements Collector<T, Multimap<K, V>, R>
{
private final Supplier<Multimap<K, V>> mapSupplier;
private final Function<? super T, ? extends K> keyMapper;
private final Function<? super T, ? extends V> valueMapper;
private final Function<Multimap<K, V>, R> resultMapper;
/**
* Creates a new MultimapCollector.
* <p>
* @param mapSupplier a function which returns a new, empty {@code Multimap} into which intermediate results will be
* inserted
* @param keyMapper a function that transforms the map keys
* @param valueMapper a function that transforms the map values
* @param resultMapper a function that transforms the intermediate {@code Multimap} into the final result
* @throws NullPointerException if any of the arguments are null
*/
public MultimapCollector(Supplier<Multimap<K, V>> mapSupplier,
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper,
Function<Multimap<K, V>, R> resultMapper)
{
Preconditions.requireThat(mapSupplier, "mapSupplier").isNotNull();
Preconditions.requireThat(keyMapper, "keyMapper").isNotNull();
Preconditions.requireThat(valueMapper, "valueMapper").isNotNull();
Preconditions.requireThat(resultMapper, "resultMapper").isNotNull();
this.mapSupplier = mapSupplier;
this.keyMapper = keyMapper;
this.valueMapper = valueMapper;
this.resultMapper = resultMapper;
}
@Override
public Supplier<Multimap<K, V>> supplier()
{
return mapSupplier;
}
@Override
public BiConsumer<Multimap<K, V>, T> accumulator()
{
return (map, entry) ->
{
K key = keyMapper.apply(entry);
if (key == null)
throw new IllegalArgumentException("keyMapper(" + entry + ") returned null");
V value = valueMapper.apply(entry);
if (value == null)
throw new IllegalArgumentException("keyMapper(" + entry + ") returned null");
map.put(key, value);
};
}
@Override
public BinaryOperator<Multimap<K, V>> combiner()
{
return (left, right) ->
{
left.putAll(right);
return left;
};
}
@Override
public Function<Multimap<K, V>, R> finisher()
{
return resultMapper;
}
@Override
public Set<Characteristics> characteristics()
{
return EnumSet.noneOf(Characteristics.class);
}
}
[...]
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
/**
* Stream collectors for Guava collections.
* <p>
* @author Gili Tzabari
*/
public final class GuavaCollectors
{
/**
* Returns a {@code Collector} that accumulates elements into a {@code Multimap}.
* <p>
* @param <T> the type of the input elements
* @param <K> the type of the map keys
* @param <V> the type of the map values
* @param <R> the output type of the collector
* @param mapSupplier a function which returns a new, empty {@code Multimap} into which intermediate results will be
* inserted
* @param keyMapper a function that transforms the map keys
* @param valueMapper a function that transforms the map values
* @param resultMapper a function that transforms the intermediate {@code Multimap} into the final result
* @return a {@code Collector} which collects elements into a {@code Multimap} whose keys and values are the result of
* applying mapping functions to the input elements
*/
public static <T, K, V, R extends Multimap<K, V>> Collector<T, ?, R> toMultimap(
Supplier<Multimap<K, V>> mapSupplier,
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper,
Function<Multimap<K, V>, R> resultMapper)
{
return new MultimapCollector<>(mapSupplier, keyMapper, valueMapper, resultMapper);
}
public static void main(String[] args)
{
Multimap<Integer, Double> input = HashMultimap.create();
input.put(10, 20.0);
input.put(10, 25.0);
input.put(50, 60.0);
System.out.println("input: " + input);
ImmutableMultimap<Integer, Double> output = input.entries().stream().collect(
GuavaCollectors.toMultimap(HashMultimap::create,
entry -> entry.getKey() + 1, entry -> entry.getValue() - 1,
ImmutableMultimap::copyOf));
System.out.println("output: " + output);
}
}
main() 输出:
input: {10=[20.0, 25.0], 50=[60.0]}
output: {51=[59.0], 11=[24.0, 19.0]}
资源
- Arjit provided an excellent resource demonstrating how to implement Collectors for other Guava collections: http://blog.comsysto.com/2014/11/12/java-8-collectors-for-guava-collections/
这是一个支持多个 ImmutableMultimap
实现的版本。请注意,第一个方法是私有的,因为它需要不安全的转换。
@SuppressWarnings("unchecked")
private static <T, K, V, M extends ImmutableMultimap<K, V>> Collector<T, ?, M> toImmutableMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction,
Supplier<? extends ImmutableMultimap.Builder<K, V>> builderSupplier) {
return Collector.of(
builderSupplier,
(builder, element) -> builder.put(keyFunction.apply(element), valueFunction.apply(element)),
(left, right) -> {
left.putAll(right.build());
return left;
},
builder -> (M)builder.build());
}
public static <T, K, V> Collector<T, ?, ImmutableMultimap<K, V>> toImmutableMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
return toImmutableMultimap(keyFunction, valueFunction, ImmutableMultimap::builder);
}
public static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
return toImmutableMultimap(keyFunction, valueFunction, ImmutableListMultimap::builder);
}
public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap(
Function<? super T, ? extends K> keyFunction,
Function<? super T, ? extends V> valueFunction) {
return toImmutableMultimap(keyFunction, valueFunction, ImmutableSetMultimap::builder);
}
虽然它没有具体回答这个问题,但值得注意的是,这个简单的模式可以帮助每个人从流构建 Guava 的不可变集合,而不需要 Collector
s(因为它们的实现相当困难制作)。
Stream<T> stream = ...
ImmutableXxx<T> collection = ImmutableXxx.copyOf(stream.iterator());
从版本 21 开始,您可以
.collect(ImmutableSet.toImmutableSet())
.collect(ImmutableMap.toImmutableMap())
.collect(Maps.toImmutableEnumMap())
.collect(Sets.toImmutableEnumSet())
.collect(Tables.toTable())
.collect(ImmutableList.toImmutableList())
.collect(Multimaps.toMultimap(...))
对于您的示例,您可以使用 Guava 的 toImmutableMap() 收集器。例如
import static com.google.common.collect.ImmutableMap.toImmutableMap;
ImmutableMap<String, ZipCode> zipCodeForName =
people.stream()
.collect(
toImmutableMap(Person::getName, p -> p.getAddress().getZipCode()));
ImmutableMap<String, Person> personForName =
people.stream()
.collect(
toImmutableMap(Person::getName, p -> p));