Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 10

Maîtrise de HashMap : Stockage et Récupération Clé-Valeur

Recommended resources

Bonjour ! Dans la leçon précédente, nous avons maîtrisé ArrayList, un outil puissant pour gérer des listes ordonnées et dynamiques. Nous avons vu que chaque élément est accessible par un index numérique (0, 1, 2, ...). Mais que faire si nous voulons accéder à nos données via une clé plus parlante, comme une chaîne de caractères ou un identifiant unique ?

Aujourd'hui, nous poursuivons notre exploration du Framework des Collections Java en nous penchant sur HashMap. C'est l'implémentation la plus courante de l'interface Map, conçue pour stocker des données sous forme de paires clé-valeur. Étant donné votre expérience avec Python et PHP, ce concept devrait vous être familier : il est très similaire aux dictionnaires (dict) en Python ou aux tableaux associatifs en PHP. L'objectif de cette leçon est de vous apprendre à utiliser HashMap pour stocker, récupérer et manipuler efficacement des données associatives.

1. Le concept de Map : une association Clé-Valeur

Contrairement à une List où l'on accède aux éléments par leur position, une Map permet d'associer une valeur à une clé unique. Vous utilisez la clé pour stocker la valeur, et vous utilisez la même clé pour la récupérer plus tard. C'est une structure de données extrêmement courante en développement backend pour des cas comme :

  • Stocker des paramètres de configuration.
  • Représenter un objet JSON.
  • Mettre en cache des données récupérées d'une base de données pour un accès rapide.

La vidéo suivante introduit ce concept avec d'excellentes analogies.

Apprendre Java #11 Map (HashMap / LinkedHashMap)

Cette vidéo de la chaîne JirAWS, intitulée "Apprendre Java #11 Map", explique très clairement le principe des paires clé-valeur.

Regardez la première partie, l'introduction, qui compare les Maps au format JSON et utilise l'analogie des casiers universitaires pour illustrer comment une clé unique permet d'accéder à une valeur.

HashMap est l'une des implémentations les plus populaires de l'interface Map. Ses principales caractéristiques sont :

  • Rapidité : Les opérations d'ajout (put), de récupération (get) et de suppression (remove) sont extrêmement rapides, en temps constant en moyenne, noté .
  • Non ordonnée : HashMap ne garantit pas l'ordre dans lequel les éléments sont stockés ou itérés. Si l'ordre d'insertion est important, on utilisera sa cousine LinkedHashMap.
  • Clés uniques : Chaque clé dans une HashMap doit être unique. Si vous ajoutez un élément avec une clé qui existe déjà, l'ancienne valeur sera simplement écrasée par la nouvelle.

2. Comment fonctionne HashMap ? Le rôle de hashCode() et equals()

Pour atteindre sa vitesse remarquable, HashMap utilise un mécanisme de "hachage". Sans entrer dans des détails trop complexes, voici l'idée principale, très importante pour un ingénieur logiciel :

  1. Lorsque vous insérez une paire (clé, valeur), HashMap calcule un "code de hachage" (hash code) à partir de la clé. Ce code est un simple entier.
  2. Ce code de hachage est utilisé pour déterminer un "seau" (bucket), une sorte de case mémoire, où la paire sera stockée.
  3. Lorsque vous voulez récupérer la valeur avec get(clé), HashMap recalcule le code de hachage de la clé, retrouve instantanément le bon seau et vous retourne la valeur.
Cette image illustre le fonctionnement interne d'une table de hachage comme HashMap. La méthode `hashCode()` de l'objet clé est utilisée pour calculer un index, déterminant dans quel "seau" (bucket) la paire clé-valeur sera stockée. En cas de collision (plusieurs clés ayant le même index), les éléments sont stockés dans une structure chaînée. La méthode `equals()` est alors utilisée pour trouver la bonne clé au sein de ce seau.

C'est ce qui explique l'importance cruciale de redéfinir correctement les méthodes equals() et hashCode() pour vos objets personnalisés si vous les utilisez comme clés, un point que nous avons déjà abordé avec ArrayList.


3. Création et manipulation d'une HashMap

Passons à la pratique. Les opérations de base sur une HashMap sont très intuitives. La vidéo suivante vous guide à travers chaque étape, de la création à la manipulation des données.

APPRENDRE LE JAVA #12 - LES HASHMAPS

La vidéo "APPRENDRE LE JAVA #12 - LES HASHMAPS" de la chaîne Graven - Développement est un excellent tutoriel pratique.

Suivez ces étapes dans la vidéo : Déclaration : Voyez comment déclarer et initialiser une HashMap en spécifiant le type de la clé et de la valeur. Ajout d'éléments : Apprenez à utiliser la méthode put() pour insérer des paires clé-valeur. Récupération d'un élément : Découvrez comment retrouver une valeur à l'aide de sa clé avec la méthode get(). Suppression d'éléments : Voyez comment supprimer une entrée avec remove() ou vider toute la map avec clear().

Exemple de code récapitulatif :

// Bonne pratique : déclarer avec l'interface Map
import java.util.Map;
import java.util.HashMap;

public class DemoHashMap {
    public static void main(String[] args) {
        // Crée une map pour stocker les âges des utilisateurs
        // La clé est un String (pseudo), la valeur est un Integer (âge)
        Map<String, Integer> agesUtilisateurs = new HashMap<>();

        // 1. AJOUTER avec put()
        agesUtilisateurs.put("Alice", 30);
        agesUtilisateurs.put("Bob", 25);
        agesUtilisateurs.put("Charlie", 35);
        
        // Si la clé existe, put() met à jour la valeur
        agesUtilisateurs.put("Bob", 26); 

        System.out.println("Map complète : " + agesUtilisateurs);

        // 2. RÉCUPÉRER avec get()
        Integer ageBob = agesUtilisateurs.get("Bob");
        System.out.println("Âge de Bob : " + ageBob);

        // Si la clé n'existe pas, get() retourne null
        Integer ageDavid = agesUtilisateurs.get("David");
        System.out.println("Âge de David : " + ageDavid); // Affiche null

        // 3. SUPPRIMER avec remove()
        agesUtilisateurs.remove("Charlie");
        System.out.println("Map après suppression de Charlie : " + agesUtilisateurs);

        // 4. MÉTHODES UTILES
        System.out.println("La map contient-elle la clé 'Alice' ? " + agesUtilisateurs.containsKey("Alice"));
        System.out.println("Taille de la map : " + agesUtilisateurs.size());
    }
}

Pour une référence plus détaillée des méthodes disponibles, l'article suivant est une excellente ressource.

Les maps (Map) en Java - JaaJBlog

Cet article du JaaJBlog est très bien structuré et va droit au but.

Consultez le tableau dans la section "Méthodes clés de Map" pour un aperçu complet des opérations. Notez des méthodes utiles comme getOrDefault(), qui évite de recevoir null. Regardez aussi la section "HashMap" sous "Principales implémentations" pour un résumé de ses caractéristiques.


4. Parcourir une HashMap

Puisqu'il n'y a pas d'index numérique, comment parcourir tous les éléments d'une HashMap ? Il existe trois approches principales, chacune avec son cas d'usage.

  1. Parcourir les clés (keySet()) : Utile si vous n'avez besoin que des clés.
  2. Parcourir les valeurs (values()) : Utile si seules les valeurs vous intéressent.
  3. Parcourir les paires clé-valeur (entrySet()) : C'est la méthode la plus courante et la plus efficace si vous avez besoin à la fois de la clé et de la valeur à chaque itération.

La fin de la vidéo de Graven illustre parfaitement comment utiliser keySet() et entrySet().

APPRENDRE LE JAVA #12 - LES HASHMAPS

Reprenons la vidéo de Graven - Développement.

Regardez attentivement le segment Parcourir la map. Il montre comment utiliser une boucle for-each avec keySet() pour obtenir les clés, puis avec entrySet() pour obtenir les paires Map.Entry, ce qui vous donne accès à la clé (getKey()) et à la valeur (getValue()).

Voici un exemple de code qui montre les trois types d'itération :

Map<String, Integer> scores = new HashMap<>();
scores.put("Maths", 18);
scores.put("Physique", 15);
scores.put("Info", 20);

// Méthode 1: Parcourir les clés
System.out.println("\n--- Clés ---");
for (String matiere : scores.keySet()) {
    System.out.println(matiere);
}

// Méthode 2: Parcourir les valeurs
System.out.println("\n--- Valeurs ---");
for (Integer note : scores.values()) {
    System.out.println(note);
}

// Méthode 3: Parcourir les entrées (clé + valeur) - LA MEILLEURE PRATIQUE
System.out.println("\n--- Entrées (clé/valeur) ---");
for (Map.Entry<String, Integer> entree : scores.entrySet()) {
    String matiere = entree.getKey();
    Integer note = entree.getValue();
    System.out.println(matiere + " : " + note);
}

5. Exercice pratique : Compteur de fréquence de mots

Mettons en pratique ce que nous avons appris avec un cas d'usage classique pour HashMap : compter le nombre d'occurrences de chaque mot dans un texte.

Énoncé :

  1. Créez une classe CompteurDeMots avec une méthode main.
  2. Définissez une chaîne de caractères String contenant une phrase de votre choix (par exemple, "Le Java est un langage puissant et le Java est très populaire").
  3. Créez une HashMap<String, Integer> pour stocker les mots et leur fréquence. La clé sera le mot (String) et la valeur sera son nombre d'occurrences (Integer).
  4. Transformez votre phrase en un tableau de mots. Vous pouvez utiliser la méthode toLowerCase() pour ignorer la casse, puis split(" ") pour découper la phrase par les espaces.
  5. Itérez sur ce tableau de mots. Pour chaque mot :
    • Vérifiez s'il est déjà présent comme clé dans votre HashMap.
    • S'il n'y est pas, ajoutez-le avec une valeur de 1.
    • S'il y est déjà, récupérez sa valeur actuelle, incrémentez-la de 1, et mettez à jour la map avec la nouvelle valeur.
  6. À la fin, affichez le contenu de la HashMap pour voir le résultat.

Astuce de pro : Pour l'étape 5, au lieu d'utiliser un if/else, vous pouvez utiliser la méthode map.getOrDefault(mot, 0). Elle retourne la valeur actuelle si la clé mot existe, ou 0 (la valeur par défaut que vous spécifiez) si elle n'existe pas. Cela simplifie le code.

Cliquez ici pour voir une proposition de solution
import java.util.HashMap;
import java.util.Map;

public class CompteurDeMots {
    public static void main(String[] args) {
        String phrase = "Le Java est un langage puissant et le Java est très populaire auprès des développeurs Java";

        // Création de la map pour stocker les fréquences
        Map<String, Integer> frequences = new HashMap<>();

        // Préparation du texte : tout en minuscules et sans ponctuation simple pour un comptage plus juste
        String texteNettoye = phrase.toLowerCase().replaceAll("[^a-z\\s]", "");
        
        // Découpage en mots
        String[] mots = texteNettoye.split("\\s+"); // \\s+ gère les espaces multiples

        // Itération sur chaque mot
        for (String mot : mots) {
            if (mot.isEmpty()) continue; // Ignorer les chaînes vides

            // Version classique avec if/else
            /*
            if (frequences.containsKey(mot)) {
                // Le mot existe déjà, on incrémente le compteur
                int compteur = frequences.get(mot);
                frequences.put(mot, compteur + 1);
            } else {
                // C'est la première fois qu'on voit ce mot
                frequences.put(mot, 1);
            }
            */

            // Version plus concise avec getOrDefault()
            int compteur = frequences.getOrDefault(mot, 0);
            frequences.put(mot, compteur + 1);
        }

        // Affichage des résultats
        System.out.println("Fréquence de chaque mot :");
        for (Map.Entry<String, Integer> entree : frequences.entrySet()) {
            System.out.println("'" + entree.getKey() + "' : " + entree.getValue());
        }
    }
}

Résultat attendu :

Fréquence de chaque mot :
'est' : 2
'un' : 1
'java' : 3
'des' : 1
'auprès' : 1
'et' : 1
'le' : 2
'populaire' : 1
'puissant' : 1
'langage' : 1
'très' : 1
'développeurs' : 1

Note : l'ordre d'affichage peut varier car HashMap est non ordonnée.


Conclusion

Vous avez maintenant ajouté HashMap à votre arsenal. C'est une structure de données incroyablement performante et polyvalente que vous utiliserez constamment dans vos projets backend.

Points clés à retenir :

  • HashMap stocke des paires clé-valeur, offrant un accès quasi instantané () aux données via la clé.
  • Elle est non ordonnée. L'ordre d'itération n'est pas garanti.
  • Les opérations de base sont put(key, value), get(key), et remove(key).
  • La meilleure façon de parcourir une HashMap si vous avez besoin des clés et des valeurs est d'utiliser entrySet().
  • Le bon fonctionnement de HashMap repose sur le contrat equals()/hashCode() des objets utilisés comme clés.

Dans la prochaine leçon, nous découvrirons HashSet, une collection qui garantit l'unicité de ses éléments. Vous verrez que HashSet est en fait construit sur une HashMap en interne, ce qui rendra sa compréhension très facile après la leçon d'aujourd'hui.