0

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));
}
Alexis C.
  • 91,686
  • 21
  • 171
  • 177
shoaib1992
  • 410
  • 1
  • 8
  • 26
  • Share your code, ask a question. Give us something to work with. I suggest reading [How to ask a good question](https://stackoverflow.com/help/how-to-ask) and [How to create a MCVE](https://stackoverflow.com/help/mcve) – Robin Topper May 01 '17 at 12:04
  • @RobinTopper Done – shoaib1992 May 01 '17 at 12:12
  • Dupe of: http://stackoverflow.com/questions/30021229/reverse-map-structure-using-java-8-streams?noredirect=1&lq=1? – Bert F May 01 '17 at 12:16
  • @BertF Not exactly the same situation. Here we have a key which is not inside the value. In that question the key, both before and after, was an element in the mapped values. – RealSkeptic May 01 '17 at 12:20
  • 1
    @RealSkeptic - Nice catch - I'll leave the comment since I think it provides a useful related link. – Bert F May 01 '17 at 12:23
  • now that you altered your question it doesn't look like a swapping from `Map>` to `Map>` anymore. What does `OperatorType.values()` return and what does `type.getSupportedtypes()` return? – Roland May 01 '17 at 13:37
  • The map you return is not fully unmodifiable, since the values (which are lists) are modifiable. – fps May 01 '17 at 13:50

4 Answers4

4

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)));
Community
  • 1
  • 1
Roland
  • 22,259
  • 4
  • 57
  • 84
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);
}
Flown
  • 11,480
  • 3
  • 45
  • 62
  • 1
    Upvoted, this is the most simple solution. However, although the returned map is unmodifiable, its values (which are lists) aren't. This is a flaw in the OP's question, but it might be worth proposing a fix, i.e. by using `Map.replaceAll` and then `Collections.unmodifiableMap` as in your answer. – fps May 01 '17 at 13:49
1

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());
}
shmosel
  • 49,289
  • 6
  • 73
  • 138
-1

Here is a simple solution by StreamEx

EntryStream.of(map).flatMapValues(v -> v.stream()).invert().grouping();
123-xyz
  • 619
  • 4
  • 5