Home > Enterprise >  Generate a Map from a list using Streams with Java 8
Generate a Map from a list using Streams with Java 8

Time:06-03

I have a list of String.

I want to store each string as key and the string's length as value in a Map (say HashMap).

I'm not able to achieve it.

List<String> ls = Arrays.asList("James", "Sam", "Scot", "Elich");
Map<String,Integer> map = new HashMap<>();

Function<String, Map<String, Integer>> fs = new Function<>() {
    @Override
    public Map<String, Integer> apply(String s) {
        map.put(s,s.length());
        return map;
    }
};

Map<String, Integer> nmap = ls
        .stream()
        .map(fs).
        .collect(Collectors.toMap()); //Lost here

System.out.println(nmap);

All strings are unique.

CodePudding user response:

There's no need to wrap each and every string with its own map, as the function you've created does.

Instead, you need to provide proper arguments while calling Collectors.toMap() :

  • keyMapper - a function responsible for extracting a key from the stream element.
  • valueMapper - a function that generates a value from the stream element.

Hence, you need the stream element itself to be a key we can use Function.identity(), which is more descriptive than lambda str -> str, but does precisely the same.

Map<String,Integer> lengthByStr = ls.stream()
            .collect(Collectors.toMap(
                Function.identity(), // extracting a key
                String::length       // extracting a value
            ));

In case when the source list might contain duplicates, you need to provide the third argument - mergeFunction that will be responsible for resolving duplicates.

Map<String,Integer> lengthByStr = ls.stream()
            .collect(Collectors.toMap(
                Function.identity(),   // key
                String::length,        // value
                (left, right) -> left  // resolving duplicates
            ));

CodePudding user response:

You said there would be no duplicate Strings. But if one gets by you can use distinct() to ensure it doesn't cause issues.

  • a-> a is a shorthand for using the stream value. Essentially a lambda that returns its argument.
  • distinct() removes any duplicate strings
Map<String, Integer> result = names.stream().distinct()
        .collect(Collectors.toMap(a -> a, String::length));

The other alternative for handling duplicates is in Alexander Ivanchenko's second toMap answer.

If you want to get the length of a String, you can do it immediately as someString.length(). But suppose you want to get a map of all the Strings keyed by a particular length. You can do it using Collectors.groupingBy() which by default puts duplicates in a list. In this case, the duplicate would be the length of the String.

  • use the length of the string as a key.
  • the value will be a List<String> to hold all strings that match that length.
List<String> names = List.of("James", "Sam", "Scot",
          "Elich", "lucy", "Jennifer","Bob", "Joe", "William");

Map<Integer, List<String>> lengthMap = names.stream()
        .distinct()
        .collect(Collectors.groupingBy(String::length));

lengthMap.entrySet().forEach(System.out::println);

prints

3=[Sam, Bob, Joe]
4=[Scot, lucy]
5=[James, Elich]
7=[William]
8=[Jennifer]
  • Related