Search code examples
javajava-streamtreemapone-liner

Given a list of strings, is it feasible to get in one line a map from each length to the set of strings having that length, sorted by length?


I came up with the following, which does work, but I feel like there should be a cleaner one-line way that doesn't rely on an "outer" map (result below):

public TreeMap<Integer, HashSet<String>> mapLenToString(List<String> strings){
    TreeMap<Integer, HashSet<String>> result = new TreeMap<>();
    strings.stream()
           .forEach(s -> { 
                int len = s.length();
                if (result.containsKey(len)) {
                    HashSet<String> larger = result.get(len);
                    larger.add(s);
                    result.replace(len, larger);
                }
                else {
                    HashSet<String> newSet = new HashSet<>();
                    newSet.add(s);
                    result.put(len, newSet);
                }
                
            });
    return result;
}

Solution

  • Just use the groupingBy collector. You can control the type of map and the type of set that the elements are collected into.

    TreeMap<Integer, HashSet<String>> result = strs
      .stream()
      .collect(
        Collectors.groupingBy(
          s -> s.length(),
          TreeMap::new,
          Collectors.toCollection(HashSet::new)
        )
      );
    

    Using this

    List<String> strs = Arrays.asList(
      "DEF", "ABC", "Hello world", "z", 
      "a", "q", "90", "12345678910", "ab");
    

    output is

    {1=[a, q, z], 2=[90, ab], 3=[ABC, DEF], 11=[12345678910, Hello world]}
    

    Link to repl.it


    You can also use Collectors.toSet() instead of toCollection(HashSet::new), where the default implementation is HashSet (but probably not guaranteed to be so)