1. Le problème de l’ordre dans les collections classiques
En Java, il y a toujours eu des collections qui garantissent l’ordre des éléments (par exemple, ArrayList, LinkedList, LinkedHashSet, LinkedHashMap) et d’autres où l’ordre n’est pas garanti (par exemple, HashSet, HashMap). Mais toutes ces collections avaient un inconvénient commun : bien que certaines conservent un ordre déterminé des éléments, les interfaces standard (List, Set, Map) ne fournissaient pas de méthodes universelles pour accéder aux premiers et derniers éléments ou inverser l’ordre.
Par exemple, si vous avez un List<String>, vous pouvez obtenir le premier élément via list.get(0), mais pour un Set ou une Map, cette astuce ne fonctionne pas — il faut utiliser des itérateurs ou écrire du code supplémentaire. Convenez que ce n’est ni très pratique ni favorable à la lisibilité.
C’est comme si vous aviez une armoire à tiroirs, mais que pour sortir le premier ou le dernier tiroir, vous deviez recompter tous les tiroirs à chaque fois ! Il serait bien plus agréable d’avoir des poignées « premier » et « dernier », non ?
Apparition de SequencedCollection et des interfaces associées
Dans Java 21, de nouvelles interfaces de collections sont apparues :
- SequencedCollection<E>
- SequencedSet<E>
- SequencedMap<K, V>
Ces interfaces étendent les collections standard et introduisent une approche unifiée pour travailler avec l’ordre des éléments. On peut désormais écrire du code universel pour n’importe quelles collections où l’ordre compte, sans se soucier de l’implémentation concrète.
De quoi s’agit-il ?
- SequencedCollection — une collection où les éléments ont un ordre défini, et où vous pouvez facilement obtenir le premier et le dernier élément, ainsi qu’inverser l’ordre.
- SequencedSet — la même idée, mais pour les ensembles (éléments uniques).
- SequencedMap — la même idée, mais pour les mappages (clé-valeur).
Quelles collections les implémentent désormais ?
Dans Java 21, les nouvelles interfaces sont implémentées par les collections standard suivantes :
- ArrayList, LinkedList → SequencedCollection
- LinkedHashSet, TreeSet → SequencedSet
- LinkedHashMap, TreeMap → SequencedMap
Cela signifie que si vous utilisez déjà ces collections, vous bénéficiez automatiquement de ces nouvelles possibilités !
2. Méthodes principales de SequencedCollection, SequencedSet, SequencedMap
Méthodes de SequencedCollection
E getFirst(); // Obtenir le premier élément
E getLast(); // Obtenir le dernier élément
SequencedCollection<E> reversed(); // Obtenir la collection en ordre inverse
Méthodes de SequencedSet
Les mêmes méthodes que SequencedCollection, plus tout ce que fait Set.
Méthodes de SequencedMap
Map.Entry<K, V> firstEntry(); // Premier élément (clé-valeur)
Map.Entry<K, V> lastEntry(); // Dernier élément (clé-valeur)
SequencedMap<K, V> reversed(); // Map en ordre inverse
3. Exemples d’utilisation des nouvelles interfaces
Exemple 1 : obtenir le premier et le dernier élément
import java.util.*;
public class SequencedDemo {
public static void main(String[] args) {
SequencedCollection<String> sc = new ArrayList<>();
sc.add("Java");
sc.add("Python");
sc.add("Kotlin");
// Obtenir le premier et le dernier élément
String first = sc.getFirst(); // "Java"
String last = sc.getLast(); // "Kotlin"
System.out.println("Premier: " + first);
System.out.println("Dernier: " + last);
}
}
Sortie :
Premier: Java
Dernier: Kotlin
Exemple 2 : inversion de la collection
SequencedCollection<String> sc = new LinkedList<>();
sc.add("A");
sc.add("B");
sc.add("C");
SequencedCollection<String> reversed = sc.reversed();
System.out.println(reversed); // [C, B, A]
À noter : reversed() renvoie une vue de la collection en ordre inverse. Si vous modifiez la collection d’origine, la vue inversée changera aussi !
Exemple 3 : travail avec SequencedSet
SequencedSet<Integer> set = new LinkedHashSet<>();
set.add(100);
set.add(200);
set.add(300);
System.out.println("Premier élément: " + set.getFirst()); // 100
System.out.println("Dernier élément: " + set.getLast()); // 300
SequencedSet<Integer> reversedSet = set.reversed();
System.out.println(reversedSet); // [300, 200, 100]
Exemple 4 : travail avec SequencedMap
SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.put("apple", 5);
map.put("banana", 3);
map.put("cherry", 7);
Map.Entry<String, Integer> first = map.firstEntry();
Map.Entry<String, Integer> last = map.lastEntry();
System.out.println("Premier: " + first.getKey() + " = " + first.getValue()); // apple = 5
System.out.println("Dernier: " + last.getKey() + " = " + last.getValue()); // cherry = 7
SequencedMap<String, Integer> reversedMap = map.reversed();
System.out.println(reversedMap); // {cherry=7, banana=3, apple=5}
4. Quel lien avec votre application ?
Supposons que, dans votre application pédagogique, vous stockiez la liste des utilisateurs qui se sont connectés et que vous vouliez obtenir rapidement le premier et le dernier utilisateur (par exemple, pour afficher « qui s’est connecté en premier » et « qui est le dernier »). Auparavant, il fallait écrire quelque chose comme :
List<String> users = new ArrayList<>();
// ... on ajoute des utilisateurs
String first = users.get(0);
String last = users.get(users.size() - 1);
Mais si la collection n’est pas une liste, et par exemple un LinkedHashSet (où les éléments sont uniques et l’ordre est conservé), cette astuce ne fonctionne plus :
Set<String> users = new LinkedHashSet<>();
// ... on ajoute des utilisateurs
// Comment obtenir le premier ? Uniquement via un itérateur :
String first = users.iterator().next();
// Et le dernier ? Il faut parcourir tous les éléments !
Désormais, c’est plus simple et plus universel :
SequencedSet<String> users = new LinkedHashSet<>();
// ... on ajoute des utilisateurs
String first = users.getFirst();
String last = users.getLast();
Cela réduit non seulement la quantité de code, mais le rend également plus lisible et plus sûr.
5. Schéma des nouvelles interfaces
classDiagram
Collection <|-- SequencedCollection
List <|-- SequencedCollection
Set <|-- SequencedSet
Map <|-- SequencedMap
SequencedCollection <|-- SequencedSet
SequencedSet <|-- LinkedHashSet
SequencedSet <|-- TreeSet
SequencedCollection <|-- ArrayList
SequencedCollection <|-- LinkedList
SequencedMap <|-- LinkedHashMap
SequencedMap <|-- TreeMap
6. Points utiles
Avantages pratiques de SequencedCollection
- Interface unifiée pour gérer l’ordre : Plus besoin de se souvenir où get(0) existe, où un itérateur est nécessaire, et où il est impossible d’obtenir le premier élément.
- Pratique avec les files et les piles : Obtenez facilement l’élément de tête (head) et de queue (tail).
- Sécurité et lisibilité : Moins d’erreurs liées à une utilisation incorrecte des collections ; le code devient auto-documenté.
- Inversion rapide de la collection : La méthode reversed() permet d’obtenir facilement l’ordre inverse sans manipulations manuelles.
- Simplicité de maintenance et d’évolution du code : Si, à l’avenir, vous souhaitez remplacer par exemple un ArrayList par un LinkedHashSet, le code utilisant SequencedCollection n’aura pas à être réécrit.
Particularités d’implémentation
- Toutes les collections n’implémentent pas SequencedCollection : Par exemple, HashSet et HashMap ne garantissent pas l’ordre, ils n’implémentent donc pas ces nouvelles interfaces.
- Les méthodes peuvent lever des exceptions : Si la collection est vide, appeler getFirst() ou getLast() provoquera une NoSuchElementException. N’oubliez pas de vérifier que la collection n’est pas vide !
- reversed() — c’est une vue, pas une copie : Les modifications de la collection d’origine se reflètent dans la vue inversée, et inversement.
- Compatibilité : Les nouvelles interfaces sont disponibles uniquement à partir de Java 21. Si vous utilisez une version de Java plus ancienne, ces fonctionnalités ne sont pas disponibles (c’est une excellente raison de mettre à jour !).
- Génériques : Toutes les nouvelles interfaces prennent pleinement en charge les génériques, vous pouvez donc travailler avec n’importe quel type de données.
7. Erreurs courantes avec SequencedCollection
Erreur n° 1 : Attendre la prise en charge de l’ordre de collections qui ne le garantissent pas. Si vous essayez de caster un HashSet en SequencedSet, vous aurez une erreur de compilation — HashSet n’a pas d’ordre et n’implémente pas cette interface.
Erreur n° 2 : Ignorer les collections vides. Appeler getFirst() ou getLast() sur une collection vide lèvera une exception. Avant d’utiliser ces méthodes, vérifiez que la collection n’est pas vide :
if (!sc.isEmpty()) {
String first = sc.getFirst();
}
Erreur n° 3 : Malentendus autour de reversed(). La méthode reversed() renvoie une vue, pas une copie. Si vous modifiez la vue inversée, la collection d’origine changera également (et inversement). Cela peut mener à des résultats inattendus si vous ne vous attendez pas à ce comportement.
Erreur n° 4 : Utiliser les nouvelles interfaces sur d’anciennes versions de Java. Si votre projet est compilé avec une version inférieure à Java 21, le compilateur ne trouvera pas ces interfaces. Vérifiez la version du JDK dans les paramètres du projet !
GO TO FULL VERSION