Java-8: Stream How to convert on Map<K, List<D>> to Map<D, List<K>>

大兔子大兔子 提交于 2019-12-08 13:07:07

问题


I've just started looking at Java 8 and to try out lambdas, I have an use case for the above problem, which I am solving using the usual for loop which is very lengthy and hard to read

My Existing code

private static Map<DataType, List<OperatorType>> buildmap() {

    Map<DataType, List<OperatorType>> map = Maps.newHashMap();

    for (OperatorType type : OperatorType.values()) {
        List<DataType> supportedTypes = type.getSupportedtypes();
        supportedTypes.forEach(datatype -> {
            if (map.containsKey(datatype)) {
                List<OperatorType> list = map.get(datatype);
                list.add(type);
                map.put(datatype,
                        list);
            } else {
                map.put(datatype,
                        new ArrayList<OperatorType>() {
                            private static final long serialVersionUID = 1L;

                            {
                                add(type);
                            }
                        });
            }
        });
    }

    return Collections.unmodifiableMap(new LinkedHashMap<>(map));
}

回答1:


Maybe something like this helps:

<D, K> Map<D, List<K>> swap(Map<K, List<D>> map) {
  return map.entrySet().stream()
                       .flatMap(e -> e.getValue().stream()
                                                 .map(v -> new SimpleEntry<>(v, e.getKey())))
     .collect(Collectors.groupingBy(Entry::getKey, 
                                    Collectors.mapping(Entry::getValue,
                                                       Collectors.toList())));
}

Streams are not always the easiest solution to all kinds of problems. See @Flown's answer for a more understandable and easy non-stream solution.

You could write Flown's answer also "Stream-like" by using .forEach, but it doesn't really get more readable (assuming values() and getSupportedTypes() both return a List) and I wouldn't recommend it just to say that you are using streams:

OperatorType.values()
  .forEach(type -> type.getSupportedTypes()
    .forEach(dataType -> map.computeIfAbsent(dataType, dt -> new ArrayList<>())
                                                .add(type)));



回答2:


A Stream solution would require some flatmapping and tuple creation.

You should stick to nested loops.

private static Map<DataType, List<OperatorType>> buildmap() {
  Map<DataType, List<OperatorType>> map = new HashMap<>();

  for (OperatorType type : OperatorType.values()) {
    for (DataType dataType : type.getSupportedTypes()) {
      map.computeIfAbsent(dataType, dt -> new ArrayList<>()).add(type);
    }
  }

  map.replaceAll((dt, ot) -> Collections.unmodifiableList(ot));

  return Collections.unmodifiableMap(map);
}



回答3:


Since you seem to be using Guava, you can use their flattening multimap collector, invert it and return the map view:

private static Map<DataType, List<OperatorType>> buildmap() {
    ImmutableListMultimap<OperatorType, DataType> flattened =
            Arrays.stream(OperatorType.values())
                    .collect(ImmutableListMultimap.flatteningToImmutableListMultimap(
                            Function.identity(),
                            o -> o.getSupportedtypes().stream()));
    return Multimaps.asMap(flattened.inverse());
}



回答4:


Here is a simple solution by StreamEx

EntryStream.of(map).flatMapValues(v -> v.stream()).invert().grouping();


来源:https://stackoverflow.com/questions/43718794/java-8-stream-how-to-convert-on-mapk-listd-to-mapd-listk

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!