创建一个 ImmutableMap<P,ImmutableMultimap<C,V>> 流收集器

Creating an ImmutableMap<P,ImmutableMultimap<C,V>> stream Collector

我经常 运行 遇到这样的情况:为了提高效率,我需要一张包含多个地图的地图。我更喜欢使用 Guava 的 ImmutableMapImmutableMultimap 来完成这个。

我已经为 Guava 借用并创建了几个 Collector 实现,因此我可以利用 Java 8 个流。例如,这是 ImmutableListMultimap 的收集器。

public static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper) {

    Supplier<ImmutableListMultimap.Builder<K, V>> supplier = ImmutableListMultimap.Builder::new;

    BiConsumer<ImmutableListMultimap.Builder<K, V>, T> accumulator = (b, t) -> b
            .put(keyMapper.apply(t), valueMapper.apply(t));

    BinaryOperator<ImmutableListMultimap.Builder<K, V>> combiner = (l, r) -> l.putAll(r.build());

    Function<ImmutableListMultimap.Builder<K, V>, ImmutableListMultimap<K, V>> finisher = ImmutableListMultimap.Builder::build;

    return Collector.of(supplier, accumulator, combiner, finisher);
}

我想为我当前的问题创建一个非常相似的收集器。我希望我的收集器创建一个 ImmutableMap<P,ImmutableMultimap<C,V>>,其中 P 是主地图的父键,C 是子地图的子键。将提供两个 Function lambda 来为每个 T 项映射这些键。

说起来容易做起来难。到目前为止,我所做的所有工作都是创建方法存根。

public static <T, P, C, V> Collector<T, ?, ImmutableMap<P, ImmutableMultimap<C,V>>> toPartitionedImmutableMultimap(
            Function<? super T, ? extends P> parentKeyMapper,
            Function<? super T, ? extends C> childKeyMapper,
            Function<? super T, ? extends V> valueMapper) {

}

因为 Guava 不可变集合构建器不允许查找,我发现自己使用可变哈希映射来查找以前捕获的值,所以我只会在 P 键不存在时创建一个新的 ImmutableMultimap。但是这个过程很快就变得头晕目眩。

有没有有效的方法来做到这一点?

我也没有看到将 Guava 构建器用于外部地图的方法。然而,涉及可变哈希图并没有那么令人眼花缭乱,不是吗? 使用 ImmutableMap.Builder 和您的 toImmutableListMultimap,可以轻松获得 ImmutableMap-Collector。 这可以在内部多图的整理器中使用,产生以下代码:

public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper) {
    final Supplier<ImmutableMap.Builder<K, V>> supplier = ImmutableMap.Builder::new;

    final BiConsumer<ImmutableMap.Builder<K, V>, T> accumulator = (b, t) -> b
            .put(keyMapper.apply(t), valueMapper.apply(t));

    final BinaryOperator<ImmutableMap.Builder<K, V>> combiner = (l, r) -> l
            .putAll(r.build());

    final Function<ImmutableMap.Builder<K, V>, ImmutableMap<K, V>> finisher = ImmutableMap.Builder::build;

    return Collector.of(supplier, accumulator, combiner, finisher);
}

public static <T, P, C, V> Collector<T, Map<P, ImmutableListMultimap.Builder<C, V>>, ImmutableMap<P, ImmutableMultimap<C, V>>> toPartitionedImmutableMultimap(
        Function<? super T, ? extends P> parentKeyMapper,
        Function<? super T, ? extends C> childKeyMapper,
        Function<? super T, ? extends V> valueMapper) {
    final Supplier<Map<P, ImmutableListMultimap.Builder<C, V>>> supplier = HashMap::new;

    final BiConsumer<Map<P, ImmutableListMultimap.Builder<C, V>>, T> accumulator = (
            map, element) -> map.computeIfAbsent(
            parentKeyMapper.apply(element),
            x -> ImmutableListMultimap.builder()).put(
            childKeyMapper.apply(element), valueMapper.apply(element));

    final BinaryOperator<Map<P, ImmutableListMultimap.Builder<C, V>>> combiner = (
            l, r) -> {
        l.putAll(r);
        return l;
    };

    final Function<Map<P, ImmutableListMultimap.Builder<C, V>>, ImmutableMap<P, ImmutableMultimap<C, V>>> finisher = map -> map
            .entrySet()
            .stream()
            .collect(
                    toImmutableMap(Map.Entry::getKey, e -> e.getValue()
                            .build()));

    return Collector.of(supplier, accumulator, combiner, finisher);
}

您尝试过直接的方法吗?

collectingAndThen(
        groupingBy(
                parentKeyMapper,
                toImmutableListMultimap(childKeyMapper, valueMapper)
        ),
        ImmutableMap::copyOf
);

更新: 上面的代码在 JDK 下运行良好,但 Eclipse 编译器对此有抱怨。这是 Eclipse 将接受的版本:

public static <T, P, C, V> Collector<T, ?, ImmutableMap<P, ImmutableMultimap<C, V>>> toPartitionedImmutableMultimap(
        Function<? super T, ? extends P> parentKeyMapper,
        Function<? super T, ? extends C> childKeyMapper,
        Function<? super T, ? extends V> valueMapper) {

    return Collectors.collectingAndThen(
            Collectors.groupingBy(
                    parentKeyMapper,
                    SO29417692.<T,C,V>toImmutableListMultimap(childKeyMapper, valueMapper)
            ),
            ImmutableMap::<P,ImmutableMultimap<C,V>>copyOf
    );
}