Skip to main content

Welcome to a Grasp lesson

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

Lesson 11

Manipulation des Collections Java

Recommended resources

Bonjour ! Dans la leçon précédente, nous avons exploré les structures de contrôle et la définition de méthodes, vous donnant les outils pour construire la logique de base de vos applications Java. Vous avez appris à prendre des décisions avec if et switch, à répéter des actions avec des boucles, et à organiser votre code en blocs réutilisables.

Aujourd'hui, nous allons nous attaquer à un sujet fondamental pour tout développeur backend : la gestion de groupes de données. L'objectif est de manipuler les collections Java (List, Set, Map) et d'identifier leurs cas d'utilisation optimaux. Tandis que les tableaux que nous avons brièvement vus sont de taille fixe, les collections offrent la flexibilité et la puissance nécessaires pour gérer des données dynamiques, comme une liste de produits, un ensemble d'identifiants uniques ou des paires de configuration.

Votre expérience avec les tableaux en JavaScript/PHP et les objets/tableaux associatifs vous sera très utile. Nous allons voir comment ces concepts sont implémentés en Java de manière structurée et typée, et surtout, comment choisir la bonne collection pour garantir la performance et la maintenabilité de vos applications.

1. Introduction au Framework des Collections Java

Avant de plonger dans chaque type de collection, il est important de comprendre le cadre général dans lequel elles s'inscrivent : le Java Collections Framework (JCF). Il s'agit d'une architecture unifiée pour représenter et manipuler des collections.

Pour une introduction formelle, je vous invite à lire les deux premières sections du document suivant.

Collections - Pratique de la programmation OO

Ce document du cours de l'EPFL présente de manière académique mais très claire ce que sont les collections et pourquoi elles sont nécessaires au-delà des simples tableaux.

Lisez les sections 1 ("Introduction") et 2 ("Collections"). Concentrez-vous sur la définition des trois types que nous allons étudier : listes, ensembles et tables associatives.

Le JCF est organisé autour d'un ensemble d'interfaces (qui définissent un contrat, un "quoi") et de classes concrètes (qui fournissent l'implémentation, le "comment").

Java Collections Framework Hierarchy Diagram
Cette image illustre la hiérarchie du Java Collections Framework. Notez comment les interfaces principales (`List`, `Set`, `Queue`) héritent de `Collection`, tandis que `Map` est une branche distincte. Les classes vertes sont les implémentations que nous utilisons au quotidien.

Une bonne pratique fondamentale en Java, qui découle des principes d'architecture logicielle, est de programmer en fonction des interfaces, et non des implémentations.

// BONNE PRATIQUE
List<String> names = new ArrayList<>();

// MAUVAISE PRATIQUE (moins flexible)
// ArrayList<String> names = new ArrayList<>();

En déclarant la variable names comme une List, vous vous laissez la liberté de changer l'implémentation plus tard (par exemple, passer à une LinkedList) sans avoir à modifier le reste du code qui utilise cette liste. C'est une première étape vers une architecture logicielle souple.


2. Les Listes : des séquences ordonnées

Une List est une collection ordonnée qui autorise les doublons. Les éléments sont accessibles via un index (commençant à 0), ce qui la rend très similaire aux tableaux que vous connaissez en JavaScript ou aux tableaux indexés en PHP.

La vidéo suivante vous donnera une introduction concrète aux listes et à leurs méthodes de base.

Apprendre Java #10 Collections (List / Set)

Cette vidéo de la chaîne JirAWS explique simplement ce qu'est une List, comment l'utiliser et le concept important d'indexation.

Regardez la vidéo de 00:54 à 05:13. Portez attention aux points suivants : La métaphore de la TodoList pour comprendre le concept de liste. Les méthodes courantes : add(), get(), remove(), size(). L'explication de l'indexation qui commence à zéro (N-1).

Choisir la bonne implémentation de List

La question clé est : quand utiliser ArrayList et quand utiliser LinkedList ? La réponse réside dans leurs performances, qui découlent de leur structure interne.

  • ArrayList : Basée sur un tableau interne.

    • Accès rapide aux éléments par index (get) : complexité en O(1).
    • Ajouts/suppressions à la fin : rapides (O(1) amorti).
    • Ajouts/suppressions au milieu : lents (O(n)), car il faut décaler tous les éléments suivants.
  • LinkedList : Basée sur une liste doublement chaînée (chaque élément a un pointeur vers le précédent et le suivant).

    • Accès aux éléments par index (get) : lent (O(n)), car il faut parcourir la liste depuis le début ou la fin.
    • Ajouts/suppressions au début ou à la fin : très rapides (O(1)).

Pour une analyse détaillée des complexités, consultez la ressource suivante, un excellent "aide-mémoire" pour développeur.

Java Collections Cheatsheet

Ce "cheatsheet" de Raphaël Lemaitre est une ressource précieuse qui résume les performances des différentes collections. Nous l'utiliserons tout au long de cette leçon.

Consultez la section "List - Listes ordonnées avec doublons". Comparez les tableaux de complexité pour ArrayList et LinkedList pour les opérations get, add et remove.

Règle pratique : Dans 95% des cas, ArrayList est le bon choix. C'est l'implémentation par défaut pour une liste à usage général. Utilisez LinkedList uniquement si vous effectuez très fréquemment des ajouts ou des suppressions au début de la liste (par exemple, pour implémenter une file d'attente ou une pile).

// Scénario : stocker une liste d'articles à afficher dans l'ordre.
// L'accès par index pour l'affichage sera fréquent. ArrayList est idéal.
List<Article> articles = new ArrayList<>();
articles.add(new Article("Titre 1", "Contenu..."));
articles.add(new Article("Titre 2", "Contenu..."));

// Récupérer le premier article
Article firstArticle = articles.get(0); 

// Itérer pour l'affichage
for (Article article : articles) {
    System.out.println(article.getTitle());
}

3. Les Sets : des collections d'éléments uniques

Un Set est une collection qui n'autorise pas les doublons. C'est sa caractéristique fondamentale. La plupart des implémentations sont non ordonnées. Ce concept est identique au Set en JavaScript.

Reprenons la vidéo précédente pour voir la différence majeure entre une List et un Set.

Apprendre Java #10 Collections (List / Set)

La suite de la vidéo compare directement une liste et un ensemble en essayant d'y insérer des doublons. C'est la meilleure façon de voir leur différence fondamentale.

Regardez la section de 05:13 à 06:50. Observez comment le Set refuse l'ajout d'un élément déjà présent.

Choisir la bonne implémentation de Set

  • HashSet : Basé sur une table de hachage.

    • Performances excellentes pour add, remove, contains : complexité en O(1).
    • Aucune garantie sur l'ordre des éléments.
    • Nécessite que les objets stockés implémentent correctement les méthodes equals() et hashCode().
  • LinkedHashSet : Hérite de HashSet mais maintient en plus une liste chaînée des éléments.

    • Performances quasi identiques à HashSet (O(1)).
    • Garantit que l'ordre d'itération est l'ordre d'insertion.
  • TreeSet : Basé sur une structure d'arbre (Red-Black Tree).

    • Performances pour add, remove, contains en O(log n). Plus lent que HashSet.
    • Maintient les éléments triés selon leur ordre naturel ou un Comparator fourni.

Consultez le cheatsheet pour voir les détails.

Java Collections Cheatsheet

Le cheatsheet nous aide à nouveau à comparer les implémentations de Set.

Lisez la section "Set - Collections sans doublons". Notez bien la différence de complexité et surtout la colonne "Ordre" pour HashSet, LinkedHashSet et TreeSet.

Règle pratique : Utilisez HashSet par défaut lorsque vous avez besoin d'unicité et que l'ordre n'a pas d'importance. Si l'ordre d'insertion est important, optez pour LinkedHashSet. TreeSet est plus rare, réservé aux cas où vous avez besoin que la collection soit constamment triée.

// Scénario : Gérer les tags d'un article de blog. L'ordre importe peu, mais chaque tag doit être unique.
// Un Set est parfait pour cela.
Set<String> tags = new HashSet<>();
tags.add("java");
tags.add("spring-boot");
tags.add("backend");

// Essayer d'ajouter un doublon. L'opération sera ignorée.
boolean isAdded = tags.add("java"); // isAdded sera false, le Set reste inchangé.

System.out.println(tags); // Affiche [java, backend, spring-boot] (l'ordre n'est pas garanti)

4. Les Maps : des associations clé-valeur

Une Map est une collection qui stocke des paires clé-valeur. Chaque clé doit être unique. C'est l'équivalent des tableaux associatifs en PHP ou des objets/Map en JavaScript. C'est une des structures de données les plus utiles en programmation.

La vidéo suivante est entièrement dédiée aux Map et explique très bien leur fonctionnement.

Apprendre Java #11 Map (HashMap / LinkedHashMap)

Cette vidéo de JirAWS est consacrée aux Maps. Elle utilise une excellente analogie et montre un cas d'usage très parlant.

Regardez la vidéo jusqu'à 05:35. Concentrez-vous sur : L'analogie des casiers pour comprendre le principe clé-valeur (00:18). Les méthodes essentielles : put(), get(), containsKey() (01:47). La différence d'ordre entre HashMap et LinkedHashMap (02:40). Le cas pratique de regroupement de personnes par mois de naissance (04:00).

Choisir la bonne implémentation de Map

Le choix suit la même logique que pour les Set :

  • HashMap : Basé sur une table de hachage.

    • Performances excellentes pour put et get : O(1).
    • Aucune garantie sur l'ordre.
    • Les clés doivent implémenter equals() et hashCode().
  • LinkedHashMap : Hérite de HashMap avec une liste chaînée.

    • Performances en O(1).
    • Maintient l'ordre d'insertion des clés.
  • TreeMap : Basé sur un arbre.

    • Performances en O(log n).
    • Maintient les clés triées.

Java Collections Cheatsheet

Un dernier passage par notre cheatsheet pour valider notre compréhension des Maps.

Lisez la section "Map - Associations clé-valeur" et comparez les caractéristiques de HashMap, LinkedHashMap et TreeMap.

Règle pratique : HashMap est le choix par défaut pour la plupart des besoins de mapping clé-valeur. Utilisez LinkedHashMap si vous devez itérer sur les entrées dans l'ordre où elles ont été ajoutées (utile pour certains caches). TreeMap est pour les cas où vous avez besoin d'un accès rapide à des clés triées.

// Scénario : Stocker une configuration simple où chaque paramètre a un nom (clé) et une valeur.
Map<String, String> config = new HashMap<>();
config.put("database.url", "jdbc:mysql://localhost:3306/mydb");
config.put("server.port", "8080");

// Récupérer une valeur par sa clé
String dbUrl = config.get("database.url");

// Itérer sur les paires clé-valeur (l'ordre n'est pas garanti)
for (Map.Entry<String, String> entry : config.entrySet()) {
    System.out.println(entry.getKey() + " = " + entry.getValue());
}

5. Comment choisir la bonne collection ?

Vous avez maintenant toutes les cartes en main. Le choix de la bonne collection est une décision d'architecture qui impacte la performance et la clarté de votre code.

La ressource Java Collections Cheatsheet que nous avons consultée contient une section de synthèse parfaite pour cela.

Java Collections Cheatsheet

Cette section synthétise tout ce que nous avons vu et vous donne des directives claires pour faire votre choix.

Lisez attentivement la section "Choix de la bonne Collection". Ces critères de sélection sont exactement ce que vous devez avoir en tête lorsque vous concevez votre code.

Pour une vision encore plus synthétique, voici un aide-mémoire visuel qui résume les caractéristiques, performances et cas d'usage de chaque type de collection. C'est un excellent document à garder sous la main.

Java Collections Cheat Sheet
Ce tableau récapitulatif est votre guide de référence rapide pour choisir la collection la plus appropriée en fonction de vos besoins : ordre, unicité, accès, et performance.

Conclusion

Vous maîtrisez désormais l'un des piliers du développement en Java. Savoir manipuler les collections et, surtout, choisir la bonne implémentation pour chaque situation est une compétence clé qui distingue un développeur professionnel.

Points clés à retenir :

  • Programmez en fonction des interfaces (List, Set, Map) pour un code plus flexible.
  • List : Pour des données ordonnées où les doublons sont permis. Le choix par défaut est ArrayList.
  • Set : Pour des données où l'unicité est cruciale et l'ordre, souvent, ne l'est pas. Le choix par défaut est HashSet.
  • Map : Pour des associations clé-valeur permettant des recherches rapides. Le choix par défaut est HashMap.
  • Les implémentations en LinkedHash... préservent l'ordre d'insertion, tandis que les Tree... maintiennent les éléments triés, au prix de performances légèrement inférieures.

Dans la prochaine leçon, nous allons rendre notre manipulation de collections encore plus puissante et expressive. Nous verrons comment utiliser les streams et les lambdas pour le traitement fonctionnel des collections. Cela vous permettra de chaîner des opérations de filtrage, de transformation et de regroupement de manière concise et lisible, une technique incontournable dans le Java moderne.