How can I use Java Stream to find the average of all values that share a key?

Just convert the first letter, which you obtain using substring, to the same case. Upper or lower, doesn't matter.

w.substring(0,1).toLowercase()

You can try with the following:

String str = "Apple arrow are very common Because bees behave Cant you come home";
Map<String, Double> map = Arrays.stream(str.split(" "))
            .collect(Collectors.groupingBy(s -> String.valueOf(Character.toLowerCase(s.charAt(0))),
                    Collectors.averagingDouble(String::length)));

The split method will split the string into an array of strings using the delimiter " ". Then, you want to group by the average of the string length. Hence, the use the of Collectors.groupingBy method and the downstream parameter Collectors.averagingDouble(String::length). Finally, given the constraints that you have described we need to group by lower case (or up case) of the first char in the String (i.e., Character.toLowerCase(s.charAt(0)))).

and then print the map:

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

If you do not need to keep the map structure you can do it in one go:

Arrays.stream(str.split(" "))
      .collect(Collectors.groupingBy(s -> String.valueOf(Character.toLowerCase(s.charAt(0))), Collectors.averagingDouble(String::length)))
      .entrySet().forEach(System.out::println);

You are almost there.

You could try the following.

  • We group by the first character of the word, converted to lowercase. This lets us collect into a Map<Character, …>, where the key is the first letter of each word. A typical map entry would then look like

    a = [ Apple, arrow, are ]
    
  • Then, the average of each group of word lengths is calculated, using the averagingDouble method. A typical map entry would then look like

    a = 4.33333333
    

Here is the code:

// groupingBy and averagingDouble are static imports from
// java.util.stream.Collectors
Map<Character, Double> map = Arrays.stream(str.split(" "))
    .collect(groupingBy(word -> Character.toLowerCase(word.charAt(0)),
        averagingDouble(String::length)));

Note that, for brevity, I left out additional things like null checks, empty strings and Locales.

Also note that this code was heavily improved responding to the comments of Olivier Grégoire and Holger below.