3

I have HashMap<String, ArrayList<String>> professions, and I want to create a SortedMap<String, Integer> where the first entry is a string same to the first entry of the hashmap, and second entry is an Integer equal to the size of the ArrayList in the first map corresponding to the string. I also want that the sortedMap is sorted by the first entry (the string).

I tryed the following:

professions.entrySet().stream()
            .collect(Collectors.groupingBy(e -> e.getKey(), TreeMap::new,
                    Collectors.mapping(e -> e.getValue().size()));

Eclipse tells me The method mapping(Function<? super T,? extends U>, Collector<? super U,A,R>) in the type Collectors is not applicable for the arguments ((<no type> e) -> {}) and also The method getValue() is undefined for the type T.

I'm not sure if my solution is wrong at all, or it's just eclipse that can't infer type.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
simondvt
  • 324
  • 3
  • 13

1 Answers1

2

You're looking for the toMap collector as opposed to the groupingBy:

Map<String, Integer> resultSet = 
     professions.entrySet()
                .stream()
                .collect(Collectors.toMap(Map.Entry::getKey,
                        e -> e.getValue().size(),
                        (left, right) -> left, TreeMap::new));
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • Thanks! Can you explain me "(left, right) -> left, TreeMap::new"? How does it work? Can't I just type "TreeMap::new"? – simondvt Jun 26 '18 at 10:52
  • @SimoneDeVita there are **three** [`toMap`](https://docs.oracle.com/javase/9/docs/api/java/util/stream/Collectors.html#toMap-java.util.function.Function-java.util.function.Function-java.util.function.BinaryOperator-java.util.function.Supplier-) collectors and there is only one that allows you to specify the map implementation you want and since you're specifically wanting a `TreeMap` hence the `TreeMap::new`. the `(left, right) -> left` will never be called in this specific case but **it is** required in order to use this overload of `toMap`. – Ousmane D. Jun 26 '18 at 10:57
  • 1
    The `(left, right) -> left` is the [merge function](https://www.concretepage.com/java/jdk-8/java-8-convert-list-to-map-using-collectors-tomap-example#merge), needed when the `toMap()` method finds duplicate keys. As said by @Aominè in this case is never called (since its impossible to have duplicate keys), but needed to call the overloaded method with the `Supplier` parameter. – Narmer Jun 26 '18 at 11:03
  • Excellent explanations! Thank you guys. – simondvt Jun 26 '18 at 11:14
  • @Aominè If he were to sort the `TreeMap` by value, could he do that inline? and how? Instead of `TreeMap::new`, can you use a comparator, `TreeMap(Comparator)` and sort by value that way? – Jack Flamp Jun 26 '18 at 11:41
  • 1
    @JackFlamp passing a comparator only allows you to control how the **keys** of the map are to be sorted not the values. that said, there are workarounds you can do. [see here](https://stackoverflow.com/questions/2864840/treemap-sort-by-value) – Ousmane D. Jun 26 '18 at 11:48
  • You may use `(left, right) -> { throw new AssertionError(); }` instead, to indicate the assumption that it will never be called. That’s preferable to silently getting *some* result, if the assumption turns out to be false (e.g., if someone modifies the code in the future or the input is an `IdentityHashMap`). – Holger Jun 26 '18 at 12:35