Java - Are there any Stream Collectors that return

2019-04-05 02:40发布

问题:

This question already has an answer here:

  • Java 8 collector for Guava immutable collections? 5 answers

I find myself wanting a variant of Collectors.toMap which returns an ImmutableMap, such that I can do:

ImmutableMap result = list.stream().collect(MyCollectors.toImmutableMap(
    tuple -> tuple._1(), tuple -> tuple._2());

(where tuple in this particular example is a Scala Tuple2)

I've just learned that such a method will be coming with Java-8 support in Guava 21 (yay!) but that sounds a good 6 months away. Does anyone know of any existing libraries (etc) which might implement this today?

ImmutableMap is not strictly required but seems the best choice as I require: lookup by key, and retaining original iteration order. Immutability is always preferred too.

Note that FluentIterable.toMap(Function) is not sufficient because I need both a key-mapping function as well as a value-mapping function.

回答1:

You don't need to write an anonymous class for this collector. You can use Collector.of instead:

public static <T, K, V> Collector<T, ?, ImmutableMap<K,V>> toImmutableMap(
            Function<? super T, ? extends K> keyMapper,
            Function<? super T, ? extends V> valueMapper) {
    return Collector.of(
               ImmutableMap.Builder<K, V>::new,
               (b, e) -> b.put(keyMapper.apply(e), valueMapper.apply(e)),
               (b1, b2) -> b1.putAll(b2.build()),
               ImmutableMap.Builder::build);
}

Or if you don't mind collecting the results into a mutable map first and then copy the data into an immutable map, you can use the built-in toMap collector combined with collectingAndThen:

ImmutableMap<String, String> result = 
     list.stream()
         .collect(collectingAndThen(
             toMap(
                 tuple -> tuple._1(), 
                 tuple -> tuple._2()),
             ImmutableMap::copyOf));


回答2:

Since I didn't find such a library of collectors yet, I'm sharing my first crack at the specific one I've needed. No bells or whistles here! (Such as handling or merging duplicate keys.)

Please feel free to suggest improvements.

/**
 * A variant of {@link Collectors#toMap(Function, Function)} for immutable maps.
 * <p>
 * Note this variant throws {@link IllegalArgumentException} upon duplicate keys, rather than
 * {@link IllegalStateException}
 * 
 * @param <T> type of the input elements
 * @param <K> output type of the key mapping function
 * @param <V> output type of the value mapping function
 * @param keyMapper  a mapping function to produce keys
 * @param valueMapper a mapping function to produce values
 * 
 * @return a {@code Collector} which collects elements into a {@code Map} whose keys and values
 *         are the result of applying mapping functions to the input elements
 *         
 * @throws IllegalArgumentException upon duplicate keys
 */
public static <T, K, V> Collector<T, ?, ImmutableMap<K,V>> toImmutableMap(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper) {
    return new Collector<T, ImmutableMap.Builder<K,V>, ImmutableMap<K,V>>() {

        public Supplier<Builder<K, V>> supplier() {
            return ImmutableMap.Builder::new;
        }

        public BiConsumer<Builder<K, V>, T> accumulator() {
            return (builder, element) -> {
                K key = keyMapper.apply(element);
                V value = valueMapper.apply(element);
                builder.put(key, value);
            };
        }

        public BinaryOperator<Builder<K, V>> combiner() {
            return (b1, b2) -> {
                b1.putAll(b2.build());
                return b1;
            };
        }

        public Function<Builder<K, V>, ImmutableMap<K, V>> finisher() {
            return builder -> builder.build();
        }

        public Set<Collector.Characteristics> characteristics() {
            return ImmutableSet.of();
        }
    };
}


标签: java-8 guava