Converting a map to another map using the Stream API

I have Map<Long, List<Member>>()

, and I want to create Map<Member, Long>

, which is calculated by iterating List<Member>

in Map.Entry<Long, List<Member>>

and summing the keys of each map entry for each member in that member list. It's easy without a non-functional way, but I couldn't find a way without writing a custom collector using Java 8 Stream API. It seems to me that I need something like Stream.collect(Collectors.toFlatMap)

, however, Collectors

there is no such method.

The best way I could find is this:

       longListOfMemberMap = new HashMap<Long, List<Member>>()
       longListOfMemberMap.put(10, asList(member1, member2));

       Map<Member, Long> collect = longListOfMemberMap.entrySet().stream()
       .collect(new Collector<Map.Entry<Long, List<Member>>, Map<Member, Long>, Map<Member, Long>>() {

        @Override
        public Supplier<Map<Member, Long>> supplier() {
            return HashMap::new;
        }

        @Override
        public BiConsumer<Map<Member, Long>, Map.Entry<Long, List<Member>>> accumulator() {
            return (memberLongMap, tokenRangeListEntry) -> tokenRangeListEntry.getValue().forEach(member -> {
                memberLongMap.compute(member, new BiFunction<Member, Long, Long>() {
                    @Override
                    public Long apply(Member member, Long aLong) {
                        return (aLong == null ? 0 : aLong) + tokenRangeListEntry.getKey();
                    }
                });
            });
        }

        @Override
        public BinaryOperator<Map<Member, Long>> combiner() {
            return (memberLongMap, memberLongMap2) -> {
                memberLongMap.forEach((member, value) -> memberLongMap2.compute(member, new BiFunction<Member, Long, Long>() {
                    @Override
                    public Long apply(Member member, Long aLong) {
                        return aLong + value;
                    }
                }));
                return memberLongMap2;
            };
        }

        @Override
        public Function<Map<Member, Long>, Map<Member, Long>> finisher() {
            return memberLongMap -> memberLongMap;

        }

        @Override
        public Set<Characteristics> characteristics() {
            return EnumSet.of(Characteristics.UNORDERED);
        }
    });

    // collect is equal to
    // 1. member1 -> 10
    // 2. member2 -> 10

      

The code in the example takes a Map> as parameter and creates a map:

parameter Map<Long, List<Member>>

:

// 1. 10 -> list(member1, member2)

      

collected value Map<Member, Long>

:

// 1. member1 -> 10
// 2. member2 -> 10

      

However, as you can see, this is much uglier than non-functional. I tried Collectors.toMap

and minified the method Stream

, but couldn't find a way to do multiple lines of code.

Which way would be the easiest and most functional for this problem?

+3


source to share


2 answers


longListOfMemberMap.entrySet().stream()
   .flatMap(entry -> entry.getValue().stream().map(
       member -> 
           new AbstractMap.SimpleImmutableEntry<>(member, entry.getKey())))
   .collect(Collectors.groupingBy(
       Entry::getKey,
       Collectors.summingLong(Entry::getValue)));

      

... although an even simpler but more imperative alternative might look like



Map<Member, Long> result = new HashMap<>(); 
longListOfMemberMap.forEach((val, members) -> 
   members.forEach(member -> result.merge(member, val, Long::sum)));

      

+6


source


I'll just point out that the code you posted can be written much more succinctly by relying on Collector.of

and turning your anonymous classes into lambdas:

Map<Member, Long> result = longListOfMemberMap.entrySet().stream()
    .collect(Collector.of(
        HashMap::new,
        (acc, item) -> item.getValue().forEach(member -> acc.compute(member,
            (x, val) -> Optional.ofNullable(val).orElse(0L) + item.getKey())),
        (m1, m2) -> {
          m1.forEach((member, val1) -> m2.compute(member, (x, val2) -> val1 + val2));
          return m2;
        }
     ));

      



It's still cumbersome, but at least not overwhelmingly.

0


source







All Articles