意外绑定 - 为什么我不能在此收集器中使用类似 T 扩展集合的内容?



嗨,我已经编写了这个自定义收集器,用于将元素的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);
}
}

现在我希望能够通过ListSet

我想把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(...)是如何实现的,那么你可以随时查看源代码。

最新更新