Exercice de style : tableau de fréquences en Java avec les Streams

Christophe Vaudry
norsys-octogone
Published in
4 min readFeb 1, 2024

Quand vous avez une collection d’éléments, vous pouvez avoir besoin de connaître la fréquence de chaque élément, c’est-à-dire le nombre de fois où cet élément apparaît dans cette collection. En anglais on parle de frequencies map et en français de tableau de fréquences.

Ainsi un tableau de fréquences d’une collection est un tableau associatif dans lequel chaque clé est un élément de cette collection initiale auquel est associé sa fréquence d’apparition dans cette dernière.

Par exemple, pour la liste de chaînes de caractères suivantes [“Friday”, “Thursday”, “Thursday”, “Saturday”, “Thursday”, “Thursday”, “Monday”, “Saturday”, “Friday”, “Saturday”], on aura le tableau de fréquences suivant { “Monday”: 1, “Thursday”: 4, “Friday”: 2, “Saturday”: 3 }.

Avant Java 8 et les streams, vous deviez parcourir la liste d’éléments avec une boucle for ou while et compter la fréquence d’un élément en remplissant un tableau associant élément et fréquence. Cela nous donne un code similaire à ce qui suit pour la liste de jour de la semaine précédente.

Code pour créer un tableau de fréquence avec une boucle for

Sous forme de script à lancer avec JBang ou JShell :

Ce qui nous donne comme résultat si on l’exécute avec JBang :

Capture d’écran du résultat de l’exécution du script précédent

Si on généralise ce code et qu’on le met dans une méthode, cela nous donne un code similaire à ce qui suit.

Généralisation du code initiale pour créer un tableau de fréquence avec une boucle for

T étant le type des éléments de la liste et donc également celui des clés du tableau de fréquences.

Avec les Streams on peut écrire une solution plus déclarative en utilisant les Collectors appropriés. En effet, il y a le Collectors groupingBy qui a un fonctionnement similaire pour les streams à celui de GROUP BY en SQL : il nous permet de grouper des éléments d’une collection par rapport à une certaine propriété et retourne un tableau associatif (une instance de Map).

Nous souhaitons regrouper tous les éléments par identité et pour chaque regroupement compter le nombre d’occurrences.

groupingBy a plusieurs surcharges, celle qui prend une fonction de classification (quel est le critère de regroupement des éléments ?) et une instance de Collectors qui applique une opération d’agrégation sur ce regroupement, ici compter le nombre d’éléments du regroupement. Java nous fournit respectivement à cette fin la fonction identity() et le Collectors counting().

Appliquer à notre liste de jours, cela donne un code similaire à ce qui suit :

Code pour créer un tableau de fréquence avec groupingBy et counting

Sous forme de script à lancer avec JBang ou JShell :

Ce qui nous donne comme résultat si on l’exécute avec JBang :

Capture d’écran du résultat de l’exécution du script précédent

Si on généralise dans une méthode cela nous donne un code similaire à ce qui suit :

Généralisation du code initiale pour créer un tableau de fréquence avec groupingBy et counting

Le lecteur attentif aura remarqué que dans l’exemple avec la boucle for on utilisait des Integer alors qu’ici on utilise des Long pour la fréquence d’apparition. La raison en est simple, counting() retourne des Long.

Et si je veux utiliser des int/Integer ? Dans ce cas, on va utiliser le Collectors summingInt dans groupingBy. On peut noter au passage que si on regarde l’implémentation de counting(), on voit qu’il utilise le Collectors summingLong(). Cela nous donne un code similaire à ce qui suit.

Code pour créer un tableau de fréquence avec groupingBy et summingInt

summingInt prend en paramètre une fonction qui extrait une valeur entière de l’élément. Dans notre cas, comme on souhaite juste compter les occurrences, à un élément on associe juste la valeur 1.

Sous forme de script à lancer avec JBang ou JShell :

Ce qui nous donne comme résultat si on l’exécute avec JBang :

Capture d’écran du résultat de l’exécution du script précédent

En synthèse, ci-après un script JBang avec l’ensemble des exemples précédents rassemblés.

Ce qui donne quand on l’exécute :

Voilà si vous avez besoin de créer un tableau de fréquences en Java et que vous souhaitez le faire avec les streams, vous avez des pistes pour le faire.

Cela est également l’occasion d’avoir un aperçu de ce que l’on peut effectuer comme opération avec les streams, l’exemple des tableaux de fréquences étant au final relativement simple.

J’avais initialement publié un billet sur ce thème sur mon blog personnel.

Remerciement

Je remercie mes collègues Baptiste, Damien et Thomas pour leur relecture attentive et leurs remarques.

--

--

Christophe Vaudry
norsys-octogone

Developer working for Norsys. Programming languages explorer. Know nothing.