嗨,我已经编写了这个自定义收集器,用于将元素的List
转换为Multimap
:
public class MultiMapCollector implements Collector<Entity,
Map<String, Collection<String>>, Map<String, Collection<String>>> {
@Override
public Supplier<Map<String, Collection<String>>> supplier() {
return HashMap::new;
}
@Override
public BiConsumer<Map<String, Collection<String>>, Entity> accumulator() {
return (map, e) -> map.computeIfAbsent(e.getName(),
k -> new HashSet<>()).add(e.getValue());
}
@Override
public BinaryOperator<Map<String, Collection<String>>> combiner() {
return (map1, map2) -> {
map2.keySet().forEach(key -> map1.computeIfAbsent(key, k -> new HashSet<>()).addAll(map2.get(key)));
return map1;
};
}
@Override
public Function<Map<String, Collection<String>>, Map<String, Collection<String>>> finisher() {
return Collections::unmodifiableMap;
}
@Override
public Set<Characteristics> characteristics() {
return Set.of(Characteristics.UNORDERED);
}
}
现在我希望能够通过List
或Set
。
我想把Map<String, Collection<String>>
改成Map<String, V extends Collection<String>>
但是当我这样做的时候,我得到一个模糊的错误说:
Unexpected Bounds
我做错了什么
我似乎无法理解这个问题。
您需要在类名之后添加一个泛型类型参数(我们称之为C
),而不是在implements
子句中。
,然后添加一个构造函数,在一个Supplier<C>
告诉你如何创建一个C
,和存储供应商。替换出现的所有new HashSet<>()
与get
调用供应商。
class MultiMapCollector<C extends Collection<String>> implements Collector<Entity, Map<String, C>, Map<String, C>> {
private final Supplier<C> supplier;
public MultiMapCollector(Supplier<C> supplier) {
this.supplier = supplier;
}
@Override
public Supplier<Map<String, C>> supplier() {
return HashMap::new;
}
@Override
public BiConsumer<Map<String, C>, Entity> accumulator() {
return (map, e) -> map.computeIfAbsent(e.getName(),
k -> supplier.get()).add(e.getValue());
}
@Override
public BinaryOperator<Map<String, C>> combiner() {
return (map1, map2) -> {
map2.keySet().forEach(key -> map1.computeIfAbsent(key, k -> supplier.get()).addAll(map2.get(key)));
return map1;
};
}
@Override
public Function<Map<String, C>, Map<String, C>> finisher() {
return Collections::unmodifiableMap;
}
@Override
public Set<Characteristics> characteristics() {
// now you aren't sure whether the collection is ordered or not
return Set.of();
}
}
现在每次你创建一个MultiMapCollector
,你需要传入一个供应商:
new MultiMapCollector<>(HashSet::new)
如果你想使用一个默认HashSet
当你不提供任何东西,添加静态工厂方法和构造函数私有:
public static MultiMapCollector<Set<String>> get() {
return new MultiMapCollector<>(HashSet::new);
}
public static <C extends Collection<String>> MultiMapCollector<C> with(Supplier<C> supplier) {
return new MultiMapCollector<>(supplier);
}
就像Collectors.groupingBy
是一个工厂方法。
更一般地说,您可以使用一个完整的下游收集器而不仅仅是Supplier<C>
,这就是Collectors.groupingBy
的2参数过载所做的。groupingBy
只处理分组,下游收集器(第二个参数)处理组。
这是你如何做到这一点的草图:
class MultiMapCollector<R> implements Collector<Entity, Map<String, R>, Map<String, R>> {
private final Collector<String, R, R> downstream;
private MultiMapCollector(Collector<String, R, R> downstream) {
this.downstream = downstream;
}
public static MultiMapCollector<Set<String>> get() {
return new MultiMapCollector<>(Collectors.toSet());
}
public static <R> MultiMapCollector<R> with(Collector<String, R, R> downstream) {
return new MultiMapCollector<>(downstream);
}
@Override
public Supplier<Map<String, R>> supplier() {
return HashMap::new;
}
@Override
public BiConsumer<Map<String, R>, Entity> accumulator() {
return (map, e) -> downstream.accumulator().accept(map.computeIfAbsent(e.getName(),
k -> downstream.supplier().get()), e.getValue());
}
@Override
public BinaryOperator<Map<String, R>> combiner() {
return (map1, map2) -> {
map2.keySet().forEach(key ->
downstream.combiner().apply(
map1.computeIfAbsent(key, k -> downstream.supplier().get()),
map2.get(key)
)
);
return map1;
};
}
@Override
public Function<Map<String, R>, Map<String, R>> finisher() {
return Collections::unmodifiableMap;
}
@Override
public Set<Characteristics> characteristics() {
return Set.of();
}
}
如果您希望允许使用Collection<String>
的不同实现作为映射的值,那么您需要为MultiMapCollector
定义一个类型参数。您还需要修改代码以接受某种类型的对象,例如Supplier
,它知道如何创建特定集合类型的实例。
例如:
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
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;
public class MultiMapCollector<V extends Collection<String>>
implements Collector<Entity, Map<String, V>, Map<String, V>> {
private final Supplier<? extends V> valueSupplier;
public MultiMapCollector(Supplier<? extends V> valueSupplier) {
this.valueSupplier = valueSupplier;
}
@Override
public Supplier<Map<String, V>> supplier() {
return HashMap::new;
}
@Override
public BiConsumer<Map<String, V>, Entity> accumulator() {
return (map, e) -> map.computeIfAbsent(e.getName(), k -> valueSupplier.get()).add(e.getValue());
}
@Override
public BinaryOperator<Map<String, V>> combiner() {
return (map1, map2) -> {
for (var entry : map2.entrySet()) {
map1.merge(
entry.getKey(),
entry.getValue(),
(curValue, newValue) -> {
curValue.addAll(newValue);
return curValue;
});
}
return map1;
};
}
@Override
public Function<Map<String, V>, Map<String, V>> finisher() {
return Collections::unmodifiableMap;
}
@Override
public Set<Characteristics> characteristics() {
return Set.of(Characteristics.UNORDERED);
}
}
注意MultiMapCollector
现在有了类型参数<V extends Collection<String>>
,并且代码中使用Collection<String>
的其他地方都被V
取代了。然后构造函数接受一个Supplier<? extends V>
,用于在accumulator()
消费者中创建V
的实例。
另外,请注意,我更新了combiner()
函数以使用Map#merge(...)
,尽管这不是严格必要的。
顺便说一下,如果你想实际看到Collectors.groupingBy(...)
是如何实现的,那么你可以随时查看源代码。