Search code examples
javajava-streamjava-11collectors

How can I collect a list of strings to a map, where each string is a key?


I am working on an exercise to count words in a phrase.

I have a regex I'm happy with to split the phrase into word tokens, so I can complete the work with basic loops - no problem.

But I'd like to use streams to collect the strings into a map instead of using basic loops.

I need each word as a key and, for now, I'd just like the integer 1 as the value. Having done some research online I should be able to collect the list of words into a map like so:

public Map<String, Integer> phrase(String phrase) {
    List<String> words = //... tokenized words from phrase
    return words.stream().collect(Collectors.toMap(word -> word, 1));
}

I have tried this, and several variations (casting word, using Function.identity()), but keep getting the error:

The method toMap(Function<? super T,? extends K>, Function<? super T,? extends U>) in the type Collectors is not applicable for the arguments ((<no type> s) -> {}, int)

Any example I've found to date only uses the string as the value, but otherwise indicates that this should be OK.

What do I need to change to make this work?


Solution

  • To get over the compilation error, you need:

    return words.stream().collect(Collectors.toMap(word -> word, word -> 1));
    

    however, this would result in all the values of the Map being 1, and if you have duplicate elements in words, you'll get an exception.

    You'll need to either use Collectors.groupingBy or Collectors.toMap with a merge function to handle duplicate values.

    For example

    return words.stream().collect(Collectors.groupingBy(word -> word, Collectors.counting()));
    

    or

    return words.stream().collect(Collectors.toMap(word -> word, word -> 1, Integer::sum));