Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I am frequently running into situations where I need a map of multi-maps for the sake of efficiency. I'd prefer to use Guava's ImmutableMap and ImmutableMultimap to accomplish this.

I have borrowed and created several Collector implementations for Guava so I can leverage Java 8 streams. For example, here is a collector for an 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);
}

I would like to create a very similar Collector for my current problem. I want my collector to create an ImmutableMap<P,ImmutableMultimap<C,V>>, where P is the parent key of the main map and C is the child key of the child map. Two Function lambdas would be provided to map these keys for each T item.

This is much easier said than done. All I did productive so far is create the method stub.

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) {

}

Because the Guava immutable collection builders do not allow lookups, I found myself using mutable hashmaps to be able to look up previously captured values so I would only create a new ImmutableMultimap when the P key was absent. But this process became dizzying very quickly.

Is there an efficient way to do this?

like image 848
tmn Avatar asked Mar 17 '23 13:03

tmn


1 Answers

Have you tried the straightforward approach?

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

Update: Above code works fine with JDK but Eclipse compiler is complaining about it. Here's a version that Eclipse will accept:

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
    );
}
like image 134
Misha Avatar answered Apr 15 '23 02:04

Misha