Les listes : List, ArrayList et génériques

Première leçon du cours Collections, streams et exceptions, en accès libre : environ 15 minutes, 3 exercices et 3 questions de quiz. C'est la leçon de base du catalogue ; dans l'espace étudiant, elle est réécrite pour ton profil.

Objectifs

  • Créer et manipuler une List<T> avec ArrayList
  • Distinguer une liste modifiable d'une liste immuable (List.of)
  • Parcourir et filtrer une liste sans provoquer de ConcurrentModificationException

Framework CollectionsList et ArrayListTypes génériques

Un tableau Java (int[]) a une taille fixe : impossible d’y ajouter un élément une fois créé. Dans un vrai programme (panier, liste d’élèves, lignes d’un fichier), on ne connaît pas la taille à l’avance. Le framework Collections (java.util) répond à ce besoin, et la collection la plus utilisée est la liste.

Déclarer une liste

import java.util.ArrayList;
import java.util.List;

List<String> courses = new ArrayList<>(); // interface à gauche, implémentation à droite
courses.add("pain");          // ajoute à la fin
courses.add("lait");
courses.add(0, "café");       // insère à l'index 0
System.out.println(courses);  // [café, pain, lait]
System.out.println(courses.get(1));   // pain
courses.set(1, "baguette");   // remplace l'élément d'index 1
System.out.println(courses.size());   // 3
System.out.println(courses.contains("lait")); // true

Depuis Java 21, les listes sont des collections séquencées : elles offrent des raccourcis pour les extrémités, plus lisibles que get(0) ou get(liste.size() - 1) :

courses.getFirst();           // "café"  (NoSuchElementException si la liste est vide)
courses.getLast();            // "lait"
courses.addFirst("thé");      // équivaut à add(0, "thé")
courses.reversed();           // vue en ordre inverse : [lait, baguette, café, thé]

Le <String> est un type générique : le compilateur refuse d’ajouter autre chose qu’une String, et get renvoie directement une String, sans conversion. L’opérateur diamant <> évite de répéter le type.

Les génériques n’acceptent que des objets : on écrit List<Integer>, pas List<int>. Java convertit automatiquement int en Integer (autoboxing).

Pourquoi déclarer avec List plutôt qu’ArrayList ? Pour pouvoir changer d’implémentation (par exemple LinkedList) sans toucher au reste du code. C’est le principe « programmer vers une interface ».

Le piège de remove

List<Integer> notes = new ArrayList<>(List.of(10, 20, 30));
notes.remove(1);                    // supprime l'élément d'INDEX 1 -> [10, 30]
notes.remove(Integer.valueOf(30));  // supprime la VALEUR 30 -> [10]

Listes immuables

List.of("a", "b") crée une liste non modifiable : add ou remove lèvent une UnsupportedOperationException. C’est idéal pour des constantes ou pour renvoyer des données qu’on ne veut pas voir modifiées. Pour obtenir une copie modifiable : new ArrayList<>(List.of(...)). Pour une copie immuable d’une liste existante : List.copyOf(liste).

Parcourir et supprimer en toute sécurité

for (String article : courses) {
    System.out.println("- " + article);
}

// ERREUR classique : supprimer pendant un for-each
// for (String a : courses) { if (a.startsWith("b")) courses.remove(a); }
// -> ConcurrentModificationException

courses.removeIf(a -> a.startsWith("b")); // la bonne façon

removeIf prend une condition (une lambda, que nous approfondirons dans la leçon 3) et supprime tous les éléments qui la vérifient.

À retenir

  • ArrayList est la liste par défaut : accès par index rapide, ajout en fin rapide.
  • Déclarez avec l’interface List<T>.
  • List.of = immuable ; new ArrayList<>(...) = modifiable.
  • Pas de suppression dans un for-each : utilisez removeIf.

Exercices

Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.

  1. CodeDifficulté 1Sandbox serveur2 indices

    Nettoyer une liste de mesures

    Lisez une ligne de nombres entiers séparés par des espaces. Stockez-les dans une List<Integer>, supprimez les valeurs négatives avec removeIf, puis affichez :

    [3, 4, 9]
    taille=3
    somme=16

    (la liste avec System.out.println(liste), puis sa taille, puis la somme). Une ligne vide donne une liste vide.

    import java.util.*;
    
    public class Main {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            String ligne = sc.hasNextLine() ? sc.nextLine().trim() : "";
            List<Integer> mesures = new ArrayList<>();
            // TODO : remplir la liste, supprimer les négatifs, afficher
        }
    }
  2. CodeDifficulté 2Sandbox serveur2 indices

    Liste de courses pilotée par commandes

    Chaque ligne de l’entrée est une commande :

    • ADD x : ajoute x à la fin ;
    • INSERT i x : insère x à l’index i ;
    • REMOVE x : supprime la première occurrence de x, ou affiche absent: x si elle n’existe pas.

    À la fin, affichez la liste numérotée à partir de 1 (1. pain). Si la liste est vide, affichez liste vide.

    import java.util.*;
    
    public class Main {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            List<String> courses = new ArrayList<>();
            while (sc.hasNextLine()) {
                String[] p = sc.nextLine().trim().split(" ");
                // TODO : traiter ADD, INSERT et REMOVE
            }
            // TODO : afficher la liste numérotée
        }
    }
  3. QCMDifficulté 11 indice

    Liste immuable

    Que se passe-t-il à l’exécution de List<String> l = List.of("a"); l.add("b"); ?

    • La liste contient [a, b]
    • Une UnsupportedOperationException est levée
    • Le code ne compile pas
    • add renvoie false sans rien changer

Quiz de fin de leçon

  1. Quelle déclaration est recommandée ?

    • ArrayList<String> l = new ArrayList<String>();
    • List<String> l = new ArrayList<>();
    • List l = new ArrayList();
  2. Sur une List valant [5, 6, 7], que donne remove(1) ?

    • [6, 7]
    • [5, 7]
    • [5, 6, 7]
  3. Comment supprimer tous les éléments vides d’une List modifiable ?

    • Dans un for-each avec liste.remove(s)
    • liste.removeIf(String::isEmpty)
    • liste.delete("")

La suite du cours

  1. Ensembles et dictionnaires : Set et Map18 min
  2. Lambdas, Comparator et premiers streams18 min
  3. Collectors, regroupements et Optional18 min
  4. Gérer les erreurs avec les exceptions20 min

Collections, streams et exceptions · Intermédiaire · 2 h 29