Saturday, December 31, 2016

Part 4 : Immutable Collectors - Bridging Google Guava and Java 8 (ImmutableMap)

In previous article we discussed about Collector interface. Before reading this article I would encourage you to read the previous articles about Collectors and how to write custom collectors. Below are links for them:
1.     Understanding Collector and writing custom Collectors for List, click here.
2.     Writing custom collectors for Set implementations, Click here.
3.     Immutable Collectors – Bridging  Google Guava and Java 8, click here.

Now let’s start with designing our collector for ImmutableMap. Remember till now we have built collectors which have one element. Map has two elements Key and Value. As there are two elements in and entry in Map we have to provide mapping for both of them. To map key and value we will use Function interface.

public static <T, K, V>
Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
           final Function<? super T, ? extends K> keyExtractor,
           final Function<? super T, ? extends V> valueExtractor) {

       return Collector.of(
                  ImmutableMap::builder,
                  (map, val) -> map.put(keyExtractor.apply(val),                                                     valueExtractor.apply(val)),
                  (left, right) -> left.putAll(right.build()),
                  ImmutableMap.Builder<K, V>::build);
    }

Let us understand the above code in terms of methods provided in Collector interface.

1.   Supplier<A> supplier() – Supplier creates and returns the new mutable result container. Collector.of(..) method’s first parameter is a Supplier. So Supplier for this Collector will be new instance of ImmutableMap.Builder<K, V>.

2.    BiConsumer<A, T> accumulator() – accumulator is used to insert a value into the mutable container.  Collector.of(..) method’s second parameter is BiConsumer. So BiConsumer for this Collector will add one value to the provided Supplier i.e.
      (mapval) -> map.put(keyExtractor.apply(val), 
                            valueExtractor.apply(val))

3.     BinaryOperator<A> combiner() – combiner is used to accept two partial result and combine it together. Combiner combines two partial result in our case it is left and right builders. So combine them by
(leftright) -> left.putAll(right.build())

4.     Function<A, R> finisher() – finisher performs final transformation from intermediate accumulation type A to type R. The final transformation from type A i.e. ImmutableMap.Builder<K, V> to type R i.e. ImmutableMap<K, V>. This is done by ImmutableMap.Builder<K, V>::build as built method in ImmutableMap.Builder<K, V> class return ImmutableMap<K, V>.

5.     Set<Characteristics> characteristics() – characteristics is used to determine the characteristics of this Collector. We are not providing any Characteristics.


Below is the example on using this collector.

Map<String, String> map = new HashMap<>();
       map.put("USA", "Washinton, D.C");
       map.put("Mexico", "Mexico City");
      
ImmutableMap<String, String> immutableMap =
              map.entrySet()
                        .stream()
                        .collect(
                                toImmutableMap(
                                       k -> k.getKey(),
                                v -> v.getValue()));

Click here for code of entire class.


If there are any mistakes or better way to do it, please mention in comments.

No comments:

Post a Comment

Ads Inside Post