Exercicio 17.9 Deitel

Galera, estou travado em um exercício do livro “Java, como programar”. Eu tenho ideia de como fazer ele com iteração externa, porém como o capítulo é de Lambdas e Streams, acredito que deve haver alguma maneira de fazê-lo por iteração interna. Mas como?
O exercício é baseado no código:

> public class StreamOfLines
> {
>    public static void main(String[] args) throws IOException
>    {
>       // Regex that matches one or more consecutive whitespace characters
>       Pattern pattern = Pattern.compile("\\s+"); 

>       // count occurrences of each word in a Stream<String> sorted by word
>       Map<String, Long> wordCounts = 
>          Files.lines(Paths.get("Chapter2Paragraph.txt"))
>               .map(line -> line.replaceAll("(?!')\\p{P}", ""))
>               .flatMap(line -> pattern.splitAsStream(line))
>               .collect(Collectors.groupingBy(String::toLowerCase,
>                  TreeMap::new, Collectors.counting()));
>       
>       // display the words grouped by starting letter
>       wordCounts.entrySet()
>          .stream()
>          .collect(
>             Collectors.groupingBy(entry -> entry.getKey().charAt(0), 
>                TreeMap::new, Collectors.toList()))
>          .forEach((letter, wordList) -> 
>             { 
>                System.out.printf("%n%C%n", letter);
>                wordList.stream().forEach(word -> System.out.printf(
>                   "%13s: %d%n", word.getKey(), word.getValue()));
>             });

>    }
> } // end class StreamOfLines

E a questão:
17.9 (Resumindo os caracteres em um arquivo) Modifique o programa da Figura 17.17 para resumir o número de ocorrências de cada
caractere no arquivo.

Encontrei como resolver o exercício. Vou postar a resolução aqui, caso seja útil para mais alguém. O exercício faz uso de uma ferramenta não encontrada no livro, as regex conhecidas como lookahead e lookbehind (é possível encontrar suas explicações no site: http://www.regular-expressions.info/lookaround.html

Resolução:

                 System.out.printf("\nMostrando o numero de ocorrencias por letra:");	
		
		Pattern pattern2 = Pattern.compile("(?!^)"); 
		// mostrando o numero de ocorrencias de cada caractere no arquivo (exercicio 17.9)
		wordCounts.entrySet()
			.stream()//Stream que contem MapS. Ou seja Stream<Map.Entry<String, Long>>, onde String e uma palavra e Long e a quantidade de repeticao desta.
			.map(Map.Entry::getKey)//modifica o fluxo e este passa a armazenar StringS, agora. O método getKey da classe Map.Entry pega a chave de cada elemento Map do fluxo, e elas passam a ser os novos elementos.
			.flatMap(palavra -> pattern2.splitAsStream(palavra))//quebrando as palavras em letras. Agora o conteudo do fuxo sao letras (lembrando que continuam sendo do tipo String).
			.collect(Collectors.groupingBy(Function.identity(), TreeMap::new, Collectors.counting()))//Montando um Map cujas chaves sao os elementos do fluxo e os valores associados sao a quantidade de repeticoes de cada chave.
			.forEach(
						(letra, repeticaoDaLetra) ->
						System.out.printf("\n%s: %d", letra, repeticaoDaLetra)
				);//Exibindo cada letra e numero de repeticao do documento, atraves do metodo forEach da classe Map retornada anteriormente.`