Map en Java - quelle implémentation choisir ?

Noël Besnard .

22 septembre 2026

Tableau comparant la complexité algorithmique des structures de données Java, incluant les map.

Une application Java doit souvent associer une information à une autre : un identifiant à un utilisateur, un produit à son prix ou un mot à son nombre d’occurrences. L’interface Map répond précisément à ce besoin, mais le choix entre HashMap, LinkedHashMap, TreeMap ou ConcurrentHashMap influence directement les performances et le comportement du programme. Je vous propose ici une approche pratique avec les méthodes essentielles, les différences entre implémentations et les pièges qui posent le plus souvent problème.

Retenir l’essentiel pour choisir la bonne structure

  • Map associe une clé unique à une valeur.
  • HashMap convient au cas général, avec des accès rapides et aucun ordre garanti.
  • LinkedHashMap conserve l’ordre d’insertion ou d’accès.
  • TreeMap trie les clés et offre des opérations en O(log n).
  • ConcurrentHashMap est adaptée aux accès concurrents entre plusieurs threads.
  • Une clé utilisée dans une map doit rester stable et correctement comparable.

Comprendre le rôle de Map en Java

Une map stocke des paires formées d’une clé et d’une valeur. La clé est unique, tandis que plusieurs clés peuvent pointer vers la même valeur. Si j’ajoute une nouvelle valeur avec une clé déjà présente, l’ancienne association est remplacée.

Map stocks = new HashMap<>();

stocks.put("clavier", 12);
stocks.put("souris", 25);
stocks.put("clavier", 15);

System.out.println(stocks.get("clavier")); // 15

Je recommande de déclarer la variable avec l’interface Map, puis de choisir l’implémentation au moment de l’instanciation. Cette habitude réduit le couplage et permet de remplacer facilement une structure par une autre si les besoins évoluent.

Une map n’est pas une liste. Elle ne repose pas sur une position numérique, et l’ordre de parcours dépend entièrement de l’implémentation choisie. C’est un détail qui paraît secondaire au début, mais qui devient important dès qu’une sortie est affichée, exportée ou comparée dans un test.

Les opérations à connaître

Les méthodes les plus utilisées sont put, get, remove et containsKey. Pour éviter un résultat null ambigu, j’utilise souvent getOrDefault lorsque l’absence d’une clé doit produire une valeur de remplacement.

int quantité = stocks.getOrDefault("écran", 0);

if (stocks.containsKey("souris")) {
    stocks.remove("souris");
}

for (Map.Entry entrée : stocks.entrySet()) {
    System.out.println(entrée.getKey() + " : " + entrée.getValue());
}

Les vues keySet(), values() et entrySet() permettent de parcourir respectivement les clés, les valeurs ou les associations complètes. Pour accéder aux deux éléments, entrySet est généralement le choix le plus lisible et évite de rechercher une seconde fois la valeur à partir de la clé.

HashMap pour la majorité des usages

HashMap est l’implémentation de référence lorsque je veux simplement stocker et retrouver rapidement des données. Les opérations de lecture et d’écriture offrent en moyenne une complexité proche de O(1), même si la performance réelle dépend de la qualité du hachage et du nombre d’éléments.

Map prix = new HashMap<>();

prix.put("SSD", 89.90);
prix.put("RAM", 54.50);
prix.put("Écran", 219.00);

Cette structure n’impose aucun ordre prévisible. Il ne faut donc pas compter sur l’ordre d’insertion, ni supposer que les clés seront affichées dans l’ordre alphabétique. Lorsque l’ordre compte, je choisis une autre implémentation au lieu d’ajouter une logique de tri fragile après chaque modification.

HashMap accepte généralement une clé null et plusieurs valeurs null, mais cette souplesse ne signifie pas que null est une bonne valeur métier. Dans un code applicatif, une clé absente et une clé présente avec une valeur null peuvent produire des erreurs difficiles à diagnostiquer.

Quand éviter HashMap

Je ne l’utilise pas directement dans un accès concurrent entre plusieurs threads sans protection adaptée. HashMap n’est pas synchronisée, et des opérations composées comme « vérifier puis insérer » peuvent être incohérentes lorsqu’elles sont exécutées simultanément.

Elle n’est pas non plus idéale si le programme doit afficher les éléments dans un ordre précis ou récupérer rapidement les clés immédiatement supérieures et inférieures à une valeur. Dans ces deux cas, LinkedHashMap ou TreeMap expriment mieux l’intention.

LinkedHashMap et TreeMap pour maîtriser l’ordre

LinkedHashMap conserve un parcours prévisible

LinkedHashMap reprend le fonctionnement général de HashMap et ajoute une chaîne de liens entre les entrées. Par défaut, elle conserve l’ordre d’insertion, ce qui est pratique pour générer un rapport, garder l’ordre d’un fichier de configuration ou construire une réponse stable pour une API.

Map étapes = new LinkedHashMap<>();

étapes.put("départ", "Paris");
étapes.put("escale", "Lyon");
étapes.put("arrivée", "Marseille");

Elle peut aussi fonctionner en ordre d’accès. Ce mode est intéressant pour un cache simple, car les entrées consultées récemment peuvent être déplacées en fin de parcours. Le coût reste généralement proche de celui d’une HashMap, avec un léger surcoût mémoire lié aux liens supplémentaires.

TreeMap trie les clés

TreeMap organise les clés selon leur ordre naturel ou selon un Comparator fourni par le développeur. Les opérations principales sont en O(log n), ce qui est un compromis raisonnable lorsque le tri et les recherches par intervalle sont plus importants que la vitesse moyenne d’une table de hachage.

Map niveaux = new TreeMap<>();

niveaux.put(3, "avancé");
niveaux.put(1, "débutant");
niveaux.put(2, "intermédiaire");

for (Map.Entry entrée : niveaux.entrySet()) {
    System.out.println(entrée.getKey() + " : " + entrée.getValue());
}

Le parcours donnera les clés dans l’ordre 1, 2, 3. TreeMap propose aussi des opérations utiles comme firstKey(), lastKey(), higherKey() et subMap(). Je la privilégie donc pour des scores, des dates, des intervalles ou toute donnée où la proximité entre les clés a un sens.

Implémentation Ordre Performance habituelle Usage conseillé
HashMap Aucun ordre garanti O(1) en moyenne Accès rapides par clé
LinkedHashMap Insertion ou accès O(1) en moyenne Parcours stable, cache simple
TreeMap Clés triées O(log n) Tri et recherches par intervalle

Les variantes utiles dans les applications réelles

Les trois structures précédentes couvrent la plupart des besoins, mais certaines situations demandent une implémentation spécialisée. Le bon choix dépend moins du nom de la classe que de la contrainte dominante : concurrence, clés enum, identité des objets ou durée de vie des références.

ConcurrentHashMap pour les accès concurrents

ConcurrentHashMap permet à plusieurs threads de lire et modifier une map avec des mécanismes adaptés à la concurrence. Elle fournit notamment des opérations atomiques comme putIfAbsent, computeIfAbsent et merge, très utiles pour les compteurs et les caches partagés.

ConcurrentHashMap compteur =
        new ConcurrentHashMap<>();

compteur.merge("connexion", 1, Integer::sum);
compteur.putIfAbsent("statut", 1);

Elle n’accepte pas les clés ni les valeurs null. Cette restriction peut sembler gênante, mais elle évite une ambiguïté importante dans un environnement concurrent. Pour une application mono-thread, je ne la choisis pas par réflexe : HashMap est plus simple et répond généralement mieux au besoin.

EnumMap lorsque les clés sont des constantes

EnumMap est conçue pour les clés d’un type enum. Elle est compacte, rapide et garantit un parcours selon l’ordre de déclaration des constantes. Pour représenter l’état d’une commande, les jours de la semaine ou des niveaux de priorité, elle est souvent plus claire qu’une map indexée par des chaînes de caractères.

enum Statut {
    EN_ATTENTE, EXPÉDIÉE, LIVRÉE
}

Map commandes = new EnumMap<>(Statut.class);
commandes.put(Statut.EN_ATTENTE, 18);

Les structures comme WeakHashMap ou IdentityHashMap répondent à des cas plus particuliers. Je les réserve aux besoins explicitement identifiés, car leur comportement repose sur des règles que l’équipe doit parfaitement comprendre avant de les introduire.

Éviter les erreurs qui rendent une Map imprévisible

Ne pas modifier une clé après son insertion

Une clé utilisée par HashMap doit avoir un hashCode stable et une méthode equals cohérente. Si un champ pris en compte par ces méthodes change après l’insertion, la map peut ne plus retrouver l’entrée, même si l’objet semble toujours présent.

class Utilisateur {
    private String email;

    // equals et hashCode doivent utiliser une valeur stable
}

Dans la pratique, j’utilise des clés immuables, comme String, Integer ou des records correctement conçus. C’est une précaution simple qui évite des bugs particulièrement pénibles à reproduire.

Faire la différence entre absence et valeur null

Avec certaines implémentations, get() peut renvoyer null parce que la clé n’existe pas ou parce qu’elle existe avec une valeur null. Si cette distinction compte, j’appelle containsKey avant de conclure que l’information est absente.

Lire aussi : IntelliJ IDEA Java - Le setup parfait pour démarrer en 2026 ?

Ne pas supposer qu’une opération composée est atomique

Le code suivant semble logique, mais il n’est pas sûr dans un environnement concurrent : lire une valeur, la modifier puis la réinsérer forme plusieurs étapes distinctes. Je préfère une méthode comme merge ou compute lorsqu’une mise à jour doit rester cohérente.

compteur.merge("visite", 1, Integer::sum);

Enfin, si une map ne doit plus changer après sa création, j’utilise une map immuable avec Map.of ou Map.copyOf. Cela rend l’intention visible et protège les données contre les modifications accidentelles, tout en rappelant que ces méthodes refusent les clés et valeurs null.

Une méthode simple pour prendre la bonne décision

Je commence par poser trois questions. Ai-je besoin d’un ordre de parcours ? Plusieurs threads vont-ils modifier la structure ? Les clés doivent-elles être triées ou parcourues par intervalle ? Les réponses conduisent presque toujours à la bonne implémentation.

  • HashMap si l’accès par clé est la priorité et que l’ordre n’a pas d’importance.
  • LinkedHashMap si l’ordre d’insertion ou d’accès doit rester prévisible.
  • TreeMap si les clés doivent être triées ou comparées entre elles.
  • ConcurrentHashMap si plusieurs threads partagent les mêmes données.
  • EnumMap si toutes les clés appartiennent à un enum.

Je conseille aussi de conserver le type déclaré sous la forme Map, même lorsque l’implémentation est connue. Le code reste ainsi plus flexible, les tests sont plus faciles à adapter et le changement de structure ne se propage pas inutilement dans toute l’application.

Le bon choix dépend de l’ordre, de la concurrence et de la stabilité

Il n’existe pas une map universellement meilleure. HashMap est un excellent point de départ, LinkedHashMap ajoute un ordre utile, TreeMap apporte le tri et ConcurrentHashMap répond à une contrainte de concurrence bien précise.

Mon conseil est de choisir la structure qui exprime clairement le besoin réel, puis de vérifier les propriétés des clés et les opérations exécutées par plusieurs threads. Cette discipline produit souvent un code plus fiable que l’optimisation prématurée d’un détail de performance.

Questions fréquentes

Choisissez LinkedHashMap lorsque l’ordre de parcours doit rester prévisible. Elle conserve par défaut l’ordre d’insertion et peut aussi fonctionner en ordre d’accès, notamment pour un cache simple. HashMap reste préférable si l’ordre n’a aucune importance.
TreeMap convient lorsque les clés doivent être triées ou comparées entre elles. Ses opérations principales sont en O(log n), et elle permet d’utiliser des méthodes comme firstKey(), lastKey(), higherKey() et subMap() pour les recherches par intervalle.
ConcurrentHashMap est conçue pour les accès concurrents et fournit des opérations atomiques comme putIfAbsent, computeIfAbsent et merge. Elle n’accepte ni clés ni valeurs null. Pour une application mono-thread, HashMap est généralement plus simple.
Une clé utilisée dans HashMap doit conserver un hashCode stable et des méthodes equals cohérentes. Si un champ utilisé par equals ou hashCode change après l’insertion, la map peut ne plus retrouver l’entrée. Les clés immuables comme String, Integer ou des records correctement conçus évitent ce problème.
Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

hashmap linkedhashmap treemap concurrenthashmap enummap
Autor Noël Besnard
Noël Besnard
Je m'appelle Noël Besnard et je possède 7 ans d'expérience dans le domaine de la technologie, en particulier en ce qui concerne le web, l'intelligence artificielle, les réseaux et la sécurité. Mon intérêt pour ces sujets a commencé dès mon adolescence, lorsque j'ai découvert le potentiel incroyable des nouvelles technologies pour transformer notre quotidien. J'aime explorer les défis que posent ces avancées et partager des solutions accessibles pour aider les lecteurs à mieux comprendre ces enjeux. Au fil des ans, j'ai eu l'occasion d'écrire sur divers aspects de la tech, en mettant l'accent sur la clarté et la précision. Je m'efforce de vérifier mes sources et de comparer les informations afin de proposer un contenu à jour et fiable. Mon objectif est de rendre des sujets complexes plus simples et compréhensibles, tout en suivant les dernières tendances du secteur. Je suis ravi de partager mes connaissances et d'accompagner les lecteurs dans leur exploration des technologies qui façonnent notre avenir.
Commentaires (0)
Ajouter un commentaire