Pour contextualiser, je traduis ici par tableau de fréquences l’expression anglaise de frequencies map.

Vous avez une liste d’éléments et vous voulez pour chaque élément connaître sa fréquence (le nombre de fois où il est présent dans cette liste) : ainsi à partir de cette liste d’éléments, vous désirez obtenir un tableau associatif dans lequel chaque clé est un élément de cet liste et est associé à sa fréquence dans la liste.

Avant Java 8 et les Streams, vous deviez parcourir la liste d’éléments et compter la fréquence d’un élément en maintenant un tableau associatif avec cet élément comme clé et la fréquence de cet élément comme valeur associée. Rien d’insurmontable mais cela représente un bon petit pavé de code à produire.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
	private static <T> Map<T, Long> createFrequencyMapOldSchool(List<T> elements) {
		Map<T, Long> frequency = new HashMap<>();

		for (T element : elements) {
			long previousCount = 0L;
			if (frequency.get(element) != null)
				previousCount = frequency.get(element);

			frequency.put(element, previousCount + 1);
		}

		return frequency;
	}

Avec les Streams on peut faire plus court (et plus simple si on apprécie une approche fonctionnelle).

1
2
3
	private static <T> Map<T, Long> createFrequencyMapWithStreams(List<T> elements) {
		return elements.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
	}

Il y a juste une petite limitation à cette approche telle que présentée ci-dessus : pourquoi diable utiliser nécessairement des long ? Et si je veux utiliser des int ?

Collectors.counting retourne un Long donc on ne peut pas l’utiliser directement. A la place on va utiliser java.util.stream.Collectors.summingInt (on notera que java.util.stream.Collectors.counting utilise java.util.stream.Collectors.summingLong) ce qui nous donne :

1
2
3
	private static <T> Map<T, Integer> createFrequencyMapWithStreamsAndInts(List<T> elements) {
		return elements.stream().collect(Collectors.groupingBy(Function.identity(), summingInt(element -> 1)));
	}

Voilà si vous avez besoin de créer un tableau de fréquences en Java et que vous souhaitez le faire avec un oneliner, vous avez les éléments pour le faire. Le gist avec les différents exemples en une seule classe Java.

Je me suis demandé comment faire un tableau de fréquences avec les Streams en Java, car confronté à un problème similaire en Clojure j’avais découvert la fonction frequencies et si vous pratiquez un peu les fold/reduce dans des approches fonctionnelles vous voyez assez rapidement que c’est une opération de ce type dans laquelle vous réduisez votre liste en ce tableau de fréquences.

MAJ : j’ai fait un billet sur le même thème en Python.