Interface List en Java
Collections ordonnées et accessibles par index en Java avec l'interface List et ses opérations principales.
List<E> est Collection<E> plus deux engagements supplémentaires : les éléments ont un ordre défini, et cet ordre est adressable par un index entier. Une fois que vous disposez d'un ordre et d'un index, toute une classe de méthodes devient pertinente — get(i), set(i, x), indexOf(x), subList(from, to), tri, itération en sens inverse. Ce chapitre parcourt le contrat ; les implémentations (ArrayList, LinkedList, Vector) viennent immédiatement après avec leurs propres compromis de performance.
Ce que signifie « ordonné » ici
« Ordonné » sur une List signifie que l'ordre d'insertion est préservé — l'index 0 est le premier élément que vous avez inséré, l'index size() - 1 est le dernier, et l'ajout d'un nouvel élément à la fin ne déplace rien. Ce n'est pas « trié » — une liste conserve l'ordre que vous produisez. Si vous souhaitez une itération triée, vous appelez soit Collections.sort(list) (qui mute), soit vous utilisez TreeSet / TreeMap dès le départ. Ne confondez pas les deux.
Les doublons sont autorisés. [1, 1, 2, 1] est une List<Integer> parfaitement légale.
Les méthodes que List ajoute à Collection
Tout ce que Collection déclare est toujours là — add, remove, contains, size, etc. List ajoute ensuite des opérations positionnelles et sensibles à l'ordre :
Accès positionnel
E get(int index)— élément àindex.E set(int index, E element)— remplacement, renvoyant l'ancienne valeur.void add(int index, E element)— insertion (décale les éléments suivants vers la droite).E remove(int index)— suppression par position (renvoie l'élément supprimé). Notez la surcharge avecObject—list.remove(1)appelle la versionint;list.remove(Integer.valueOf(1))appelle la versionObject.
Recherche
int indexOf(Object o)— première occurrence, ou-1.int lastIndexOf(Object o)— dernière occurrence, ou-1.
Sous-vues et itération
List<E> subList(int fromIndex, int toIndex)— une vue en direct d'une plage. La modifier modifie la liste de support (et inversement). Semi-ouverte :[from, to).ListIterator<E> listIterator()/listIterator(int index)— itérateur qui peut également se déplacer en sens inverse, obtenir l'index courant, etset/addau niveau du curseur. Le chapitre ListIterator le couvre.
Mutation en masse liée à l'ordre
default void replaceAll(UnaryOperator<E> op)— appliqueopà chaque élément en place.default void sort(Comparator<? super E> c)— trie la liste à l'aide dec(ou de l'ordre naturel sinull).boolean addAll(int index, Collection<? extends E> c)— insère toute une collection àindex.
Fabriques (Java 9+)
List.of(...)— une liste non modifiable contenant les éléments donnés. Compacte, sans allocation pour les petites tailles.List.copyOf(Collection)— un instantané non modifiable d'une autre collection.
L'égalité sur List est sensible à l'ordre
Deux listes sont égales si et seulement si elles ont la même taille, dans le même ordre, avec des éléments égaux à chaque index. List.of(1, 2) n'est pas égal à List.of(2, 1), même s'ils l'auraient été en tant que Set. C'est une règle absolue du contrat List — si vous comparez deux listes et obtenez false alors que vous ne « devriez » pas, vérifiez l'ordre en premier.
subList est une vue, pas une copie
Cela prend presque chaque apprenant par surprise une fois :
List<Integer> xs = new ArrayList<>(List.of(0, 1, 2, 3, 4, 5));
List<Integer> middle = xs.subList(2, 5); // [2, 3, 4]
middle.set(0, 99);
System.out.println(xs); // [0, 1, 99, 3, 4, 5] — xs changed!
middle.clear();
System.out.println(xs); // [0, 1, 5] — gone from xssubList renvoie une fenêtre en direct. Les lectures et les écritures passent par la liste de support. C'est extrêmement utile pour les algorithmes en place — effacer une plage, trier une plage, insérer une plage — mais cela signifie également que vous ne pouvez pas conserver une référence subList et muter ensuite le parent par tout autre chemin. La Javadoc indique que les modifications structurelles de la liste de support en dehors de la sous-liste « rendent indéfini » le comportement de la sous-liste. En pratique, ConcurrentModificationException au prochain appel.
Si vous souhaitez une tranche indépendante, copiez-la : new ArrayList<>(xs.subList(2, 5)).
Les deux surcharges de remove
Un bug courant :
List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
nums.remove(1); // removes index 1 → [10, 30]
nums.remove(Integer.valueOf(10)); // removes the value 10 → [30]La surcharge int l'emporte car int est plus spécifique que Integer. Si vous voulez dire « supprimer la valeur 10 », encadrez-la explicitement. C'est l'un des rares endroits dans le langage où les règles d'autoboxing et la résolution des surcharges entrent activement en conflit.
Tri en place
list.sort(comparator) mute la liste. Passez null pour utiliser l'ordre naturel des éléments (leur Comparable) ; passez un Comparator sinon. C'est la forme moderne — Collections.sort(list) fonctionne toujours et est identique, mais list.sort(...) est la méthode List par défaut :
List<String> names = new ArrayList<>(List.of("Linus", "Ada", "Grace"));
names.sort(null); // natural: ["Ada", "Grace", "Linus"]
names.sort(Comparator.comparingInt(String::length)); // shortest firstLe chapitre Comparable / Comparator plus loin dans cette partie est le livre de règles concernant ce que signifie null et comment construire des comparateurs pour vos propres types.
Fabriques immuables : quand add lève une exception
List.of(...), List.copyOf(...) et les listes renvoyées par Collectors.toUnmodifiableList() sont non modifiables. Elles rejettent chaque appel mutant avec UnsupportedOperationException. Elles rejettent également les éléments null. Elles sont idéales pour des données en lecture seule partagées largement :
List<String> CONSTANTS = List.of("red", "green", "blue");
CONSTANTS.add("yellow"); // throws UnsupportedOperationExceptionSi vous pensez vouloir muter ultérieurement, commencez avec new ArrayList<>(List.of(...)).
Un exemple concret : toutes les méthodes spécifiques à List
Le programme ci-dessous exerce les méthodes que List ajoute par rapport à Collection. Observez la propagation de la mutation via subList, le piège de surcharge, et la différence entre sort et replaceAll.
Quelques points à retenir à partir de la sortie :
remove(1)a supprimé20(la valeur à l'index 1) ;remove(Integer.valueOf(10))a supprimé10par valeur. Même nom de méthode, deux travaux différents selon le type statique de l'argument.- Après
mid.clear(), la liste parente est[0, 1, 5]. La vue était la plage — l'effacer a supprimé ces éléments du tableau de support. replaceAllconserve la même longueur de liste et réécrit chaque élément en place ;sortréarrange ce qui s'y trouve déjà. Ils se composent bien.
Prochaine étape
Vous connaissez maintenant le contrat List — ce qui est garanti, ce qui mute quoi, et où se trouvent les pièges. Il est temps de rencontrer l'implémentation que vous utiliserez 90 % du temps : ArrayList, adossée à un tableau redimensionnable. Même contrat, caractéristiques de performance spécifiques, et quelques extras qui lui sont propres.