ImmutableMapFactory { public static <K, L, V, W> ImmutableMap<L, W> createAndReduceCollisions( Map<K, V> baseMap, Function<? super K, ? extends L> keyMapper, Function<? super V, ? extends W> valueMapper, W valueIdentity, BinaryOperator<W> collidingKeysAccumulator) { Function<Entry<K, V>, Entry<L, W>> entryMapper = e -> new AbstractMap.SimpleEntry<>( keyMapper.apply(e.getKey()), valueMapper.apply(e.getValue())); Collector<Entry<L, W>, ?, W> entriesCollector = Collectors.reducing(valueIdentity, Entry::getValue, collidingKeysAccumulator); Map<L, W> mutableMap = baseMap.entrySet().stream() .map(entryMapper) .collect(Collectors.groupingBy(Entry::getKey, entriesCollector)); return ImmutableMap.copyOf(mutableMap); }  static ImmutableMap<L, W> create(
            Map<K, V> baseMap,
            BiFunction<? super K, ? super V, ? extends L> keyCreator,
            BiFunction<? super K, ? super V, ? extends W> valueCreator); static ImmutableMap<L, W> create(
            Map<K, V> baseMap,
            Function<? super K, ? extends L> keyMapper,
            Function<? super V, ? extends W> valueMapper); static ImmutableMap<L, W> createAndReduceCollisions(
            Map<K, V> baseMap,
            Function<? super K, ? extends L> keyMapper,
            Function<? super V, ? extends W> valueMapper,
            W valueIdentity,
            BinaryOperator<W> collidingKeysAccumulator); static ImmutableMap<L, V> createWithNewKeys(
            Map<K, V> baseMap,
            Function<? super K, ? extends L> keyMapper); static ImmutableMap<K, W> createWithNewValues(
            Map<K, V> baseMap,
            Function<? super V, ? extends W> valueMapper); static ImmutableMap<K, W> createWithNewValues(
            Map<K, V> baseMap,
            BiFunction<? super K, ? super V, ? extends W> valueMapper); static ImmutableMap<K, V> createFromKeys(
            Collection<K> keys,
            Supplier<V> valueSupplier); static ImmutableMap<K, V> createFromKeys(
            Collection<K> keys,
            Function<K, V> valueCreator); static ImmutableMap<K, V> zip(
            List<K> keys,
            List<V> values); static ImmutableMap<K, V> filter(
            Map<K, V> baseMap,
            BiPredicate<K, V> filter); static ImmutableMap<K, V> filterByKeys(
            Map<K, V> baseMap,
            Predicate<K> keyFilter); static ImmutableMap<K, V> filterByValues(
            Map<K, V> baseMap,
            Predicate<V> valueFilter); static ImmutableMap<K, V> parse(
            String s,
            Function<String, K> keyParser,
            Function<String, V> valueParser); static ImmutableMap<K, V> parseAndFilter(
            String s,
            Function<String, K> keyParser,
            Function<String, V> valueParser,
            BiPredicate<K, V> filter);  }
@Test void testCreateAndReduce() { ImmutableMap<String, Integer> toReduce = ImmutableMap.of( "A0", 1, "A1", 2, "B", 3, "A2", 4); ImmutableMap<String, Integer> reduced = ImmutableMapFactory.createAndReduceCollisions( toReduce, s -> s.substring(0, 1), i -> i, 0, Integer::sum); ImmutableMap<String, Integer> expected = ImmutableMap.of( "A", 7, "B", 3); Assertions.assertEquals(expected, reduced); }
@Test void testCreateWithUnnecessaryReduction() { ImmutableMap<String, Integer> toReduce = ImmutableMap.of( "A", 1, "B", 2, "C", 3, "D", 4); ImmutableMap<String, Integer> reduced = ImmutableMapFactory.createAndReduceCollisions( toReduce, s -> s.substring(0, 1), i -> i, 0, Integer::sum); ImmutableMap<String, Integer> expected = ImmutableMap.of( "A", 1, "B", 2, "C", 3, "D", 4); Assertions.assertEquals(expected, reduced); }