FAQ Langage JavaConsultez toutes les FAQ

Nombre d'auteurs : 41, nombre de questions : 296, dernière mise à jour : 8 mars 2017  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur le forum Java de http://java.developpez.com ainsi que l'expérience personnelle des auteurs.

Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes. Les auteurs font leur maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous souhaitez nous aider en devenant rédacteur, lisez ceci.

Sur ce, nous vous souhaitons une bonne lecture.


SommaireCollections et Streams (28)
précédent sommaire suivant
 

Les collections font partie du JDK depuis la version 1.2. Les collections proposent une série de classes, d'interfaces et d'implémentations pour gérer efficacement les données.

Pour chaque type de structure de données (liste, ensemble, association), il existe une interface et plusieurs implémentations. Chaque implémentation utilise une stratégie avec des avantages et des inconvénients. Il est important de bien comprendre les différentes stratégies pour choisir l'implémentation la plus performante en fonction de ses besoins.

Conseil
Tout d'abord, afin de minimiser la quantité de code à modifier pour changer d'implémentation, il convient de toujours faire référence aux collections en utilisant les interfaces, seule l'étape de construction faisant référence à l'implémentation.

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
// La bonne solution :  
List<Object> list = new ArrayList<>(); 
Set<Object> set = new HashSet<>(); 
Map<Object> map = new TreeMap<>(); 
// La mauvaise solution :  
ArrayList<Object> list = new ArrayList<>(); 
HashSet<Object> set = new HashSet<>(); 
TreeMap<Object> map = new TreeMap<>();

Il peut néanmoins arriver qu'on soit contraint d'utiliser la 2e solution pour avoir accès aux méthodes spécifiques à l'implémentation. En effet, les classes concrètes sont souvent plus riches que les interfaces, ce qui peut se révéler nécessaire pour tirer le meilleur parti possible d'une implémentation.

Détails
Les principaux types de collections sont :
  • java.util.Set - un ensemble : un groupe d'éléments uniques ;
  • java.util.List - une liste : une suite d'éléments ordonnés accessibles par leur indice (leur place dans la liste). Les listes ne garantissent pas l'unicité des éléments ;
  • java.util.Map - un dictionnaire ou une association : ils mémorisent une collection de couples clé-valeur. Si vous avez une clé, l'association retrouvera la valeur associée à cette clé. Les clés sont uniques, mais la même valeur peut-être associée à plusieurs clés.

Mis à jour le 15 juin 2015 bentechno bouye Clement Cunin

L'API propose trois implémentations concrètes pour les ensembles (java.util.Set). Un ensemble est un groupe d'éléments uniques.

HashSet
La classe java.util.HashSet est la plus utile des implémentations. L'ordre d'itération des éléments de cette collection est aléatoire.
Les opérations add(), remove(), contains() and size() sont exécutées en un temps constant.

TreeSet
La classe java.util.TreeSet contient un ensemble d'éléments ordonnés : elle implémente également l'interface java.util.SortedSet. Les éléments sont ordonnés en fonction de leur ordre naturel (voir java.util.Comparable), ou en fonction d'un java.util.Comparator précisé à la construction du TreeSet.
Les opérations add(), remove(), contains() and size() sont exécutées en un temps log(n).

LinkedHashSet
La classe java.util.LinkedHashSet est identique au HashSet sauf qu'elle conserve l'ordre d'insertion des éléments dans une liste doublement chaînée. L'ordre d'itération des éléments correspond à l'ordre d'insertion, l'ordre reste inchangé si l'on ajoute un élément déjà présent.
Les opérations add(), remove(), contains() and size() sont exécutées en un temps constant (mais supérieur au temps du HashSet, car il faut également gérer la liste chaînée).

Mis à jour le 15 juin 2015 bouye Clement Cunin

L'API propose deux implémentations concrètes pour les listes (java.util.List). Une liste est une suite ordonnée d'éléments. Un élément peut être ajouté à plusieurs endroits dans une même liste.

ArrayList
La classe java.util.ArrayList utilise un tableau en interne pour ranger les données. Un ArrayList fournit un accès aux éléments par leur indice très performant et est optimisé pour des opérations d'ajout/suppression d'éléments en fin de liste.

Les données de la liste sont rangées dans un tableau d'objets. Le principal intérêt d'un ArrayList étant de pouvoir facilement ajouter un élément à la liste, le tableau utilisé en interne est surdimensionné par rapport au besoin. Par exemple : un ArrayList de 5 éléments pourrait utiliser un tableau de 10 éléments, lorsqu'on ajoute un nouvel élément, on le place dans la 1re case libre du tableau interne, on n'est pas obligé d'instancier un nouveau tableau. Évidemment, à force d'ajouter des éléments, le tableau interne peut se trouver saturé, dans ce cas un nouveau tableau est créé et les anciennes valeurs sont recopiées dedans.

La gestion automatique de la taille du tableau interne ne nous empêche pas de donner un coup de main pour améliorer les performances. Lors de la création d'une liste, il est important d'utiliser un constructeur précisant la capacité (taille du tableau interne), il est en général facile d'estimer la taille moyenne de sa structure de données, on évite ainsi de passer par plusieurs dizaines de réallocations du tableau interne. De même, avant d'insérer une grande quantité d'informations, il est possible de demander de garantir une capacité minimale. Ces précautions sont d'autant plus importantes si la liste contient beaucoup d'éléments.

De plus, le tableau contenu dans un ArrayList ne fait que grandir, il ne diminue jamais même en cas de suppression d'un ou de tous les éléments de la liste. Vous devez invoquer la méthode trimToSize() de la liste pour réduire la capacité de stockage du tableau à la taille de la liste.

Code Java : Sélectionner tout
1
2
3
4
5
6
List<String> maListe = new ArrayList<>(10); // Préallocation d'un tableau de capacité 2. 
maListe.add("Toto"); // La capacité du tableau ayant été préallouée, il n'est pas redimensionné. 
maListe.add("Tata"); // Idem. 
maListe.add("Titi");  // Augmentation de la capacité de stockage du tableau. 
maListe.remove("Titi"); // La capacité de stockage du tableau reste inchangée. 
maListe.trimToSize() // Réduction de la capacité de stockage du tableau.

Les opérations size(), isEmpty(), get(), set(), iterator() sont exécutées en temps constant. Les opérations d'ajout/suppression sont exécutées en temps constant amorti (les ajouts/suppressions en fin de liste sont plus rapides).

LinkedList
La classe java.util.LinkedList utilise une liste chaînée pour ranger les données. De ce fait, l'ajout et la suppression d'éléments sont aussi rapides quelle que soit la position, mais l'accès aux valeurs par leur indice est très lent.

Compte tenu de son mode de stockage, la liste chaînée occupe, par élément, un espace mémoire plus important que lorsque ce même élément est contenu dans le tableau interne d'un ArrayList. Par contre, ici, le nombre de cellules de stockage créées est strictement identique à la taille de la liste, là où ArrayList peut manipuler un tableau plus grand que le nombre d’éléments qu'il contient.

Code Java : Sélectionner tout
1
2
3
4
5
List<String> maListe = new LinkedList<>(); 
maListe.add("Toto"); // Création d'une nouvelle cellule de stockage 
maListe.add("Tata"); // Idem. 
maListe.add("Titi"); // Idem. 
maListe.remove("Titi"); // Suppression d'une cellule de stockage.

Les opérations size(), isEmpty(), add, remove(), set(), get() sont exécutées en temps constant. Toutes les méthodes qui font référence à un indice sont exécutées en temps O(n).

Vector
La classe java.util.Vector est une classe héritée de Java 1.0. Elle n'est conservée dans l'API actuelle que pour des raisons de compatibilité ascendante et elle ne devrait pas être utilisée dans les nouveaux programmes. Dans tous les cas, il est préférable d'utiliser un ArrayList. Cette classe est thread-safe, c'est-à-dire que plusieurs threads (processus) peuvent l'utiliser en même temps sans risque.

Les données d'un Vector sont rangées dans un tableau d'objets, tout comme dans un ArrayList ; les considérations d’allocation de la capacité initiale de stockage ou lors d'agrandissements de la liste sont donc identiques. La complexité est similaire à celle de la classe ArrayList, plus le temps de synchronisation des méthodes.

Mis à jour le 15 juin 2015 bouye Clement Cunin

L'API propose cinq implémentations concrètes pour les dictionnaires ou associations (java.util.Map). Une map permet de créer un ensemble de couples clé/valeur ; on parle aussi de tableaux associatifs. La clé permet de retrouver rapidement la valeur qui lui a été associée. Les clés sont des objets uniques pouvant être null. Les valeurs peuvent être multiples et null.

HashMap
La classe java.util.HashMap est l'implémentation concrète la plus standard, elle est adaptée à la plupart des situations.

Code Java : Sélectionner tout
1
2
3
4
5
6
Map<Integer, String> monDictionnaire = new HashMap<>(); 
monDictionnaire.put(0, "Toto"); 
monDictionnaire.put(1, "Tata"); 
monDictionnaire.put(2, "Titi"); 
monDictionnaire.put(2, null); // Écrase la valeur "Titi". 
String value = monDictionnaire.get(0); // Retourne "Toto".

TreeMap
La classe java.util.TreeMap ajoute une fonction de tri des clés de la map. L'ordre des clés peut être choisi en donnant une instance de java.util.Comparator, sinon c'est l'ordre naturel des clés qui sera utilisé ; elles doivent donc implémenter java.lang.Comparable.

Code Java : Sélectionner tout
1
2
3
4
Map<Integer, String> monDictionnaire = new TreeMap<>(); 
monDictionnaire.put(0, "Toto"); 
monDictionnaire.put(1, "Tata"); 
monDictionnaire.put(2, "Titi");

Si vous avez une grande quantité de données à ajouter dans la collection, et que l'ordre des clés n'est utile qu'après l'ajout, il est plus efficace de créer un HashMap pour ajouter les éléments et de construire la TreeMap à partir de la HasMap :

Code Java : Sélectionner tout
1
2
3
4
5
6
Map<Integer, String> monDictionnaire = new HashMap<>(); 
// Toutes les opérations pour ajouter les éléments...  
monDictionnaire .put(....); 
[...] 
// ... puis on construit la TreeMap.  
monDictionnaire = new TreeMap<>(monDictionnaire);

LinkedHashMap
La classe java.util.LinkedHashMap conserve l'ordre d'ajout des clés (même principe qu'avec LinkedHashSet). Si la clé ajoutée est déjà présente, l'ordre ne change pas.

IdentityHashMap
La classe java.util.IdentityHashMap, contrairement aux autres implémentations concrètes, utilise l'opérateur == pour savoir si deux clés sont identiques. Les autres maps utilisent le résultat de la méthode equals().

WeakHashMap
La classe java.util.WeakHashMap conserve les couples en utilisant des références faibles. Donc, si la clé n'est plus référencée ailleurs dans le programme, le couple est automatiquement supprimé de la collection (voir java.lang.ref.WeakReference).

Hashtable
La classe java.util.Hashtable est une classe héritée de Java 1. Elle n'est conservée dans l'API actuelle que pour des raisons de compatibilité ascendante et elle ne devrait pas être utilisée dans les nouveaux programmes. Dans tous les cas, il est préférable d'utiliser un HashMap. Cette classe est thread-safe, c'est-à-dire que plusieurs threads (processus) peuvent l'utiliser en même temps sans risque.

Mis à jour le 15 juin 2015 bentechno bouye Clement Cunin

Pour créer une collection en lecture seule, il suffit d'invoquer une des méthodes unmodifiableXXX() de la classe java.util.Collections. Ces méthodes prennent une collection (ensemble, liste, dictionnaire) en paramètre et retournent une collection du même type qui ne peut pas être modifiée : c'est-à-dire dont les méthodes qui permettent de modifier le contenu (retrait, ajout, changement de valeur) de la collection lèvent une exception lorsqu'on les invoque. La nouvelle collection empaquète la collection d'origine ; il n'y a donc pas de duplication de données ou de structure de stockage.

  • unmodifiableCollection() - permet de créer une collection non modifiable (qui répond à l'interface java.util.Collection) ;
  • unmodifiableList() - permet de créer une liste non modifiable ;
  • unmodifiableMap() - permet de créer un dictionnaire non modifiable ;
  • unmodifiableNavigableMap() - permet de créer un dictionnaire navigable non modifiable ;
  • unmodifiableNavigableSet() - permet de créer un ensemble navigable non modifiable ;
  • unmodifiableSet() - permet de créer un ensemble non modifiable ;
  • unmodifiableSortedMap() - permet de créer un dictionnaire ordonné non modifiable ;
  • unmodifiableSortedSet() - permet de créer un ensemble ordonné non modifiable.


Par exemple :

Code Java : Sélectionner tout
1
2
List<String> maListe = Arrays.asList("Toto", "Titi", "Tata"); 
List<String> maListeLectureSeule = Collections.unmodifiableList(maListe);

Ici, maListeLectureSeule ne peut pas être modifiée : si vous invoquez maListeLectureSeule.add(), cette méthode signalera une exception de type UnsupportedOperationException. La liste maListe est contenue dans maListeLectureSeule. Si des modifications (retrait, ajout, changement de valeur) sont opérées sur maListe, elles seront répercutées sur maListeLectureSeule.

Code Java : Sélectionner tout
1
2
3
4
String value = maListeLectureSeule.get(0); // Ok. 
maListeLectureSeule.add("Tutu"); // Génère une exception de type UnsupportedOperationException. 
maListeLectureSeule.remove(1); // Génère une exception de type UnsupportedOperationException. 
maListeLectureSeule.clear(); // Génère une exception de type UnsupportedOperationException.

Attention : c'est la collection elle-même qui est en lecture seule, et non pas pas les éléments qu'elle contient. Si les éléments de la collection sont mutables, il est toujours possible de modifier les valeurs de ces éléments même en manipulant la collection en lecture seule.

Mis à jour le 21 juin 2015 bouye

Pour créer une collection synchronisée, il suffit d'invoquer une des méthodes synchronizedXXX() de la classe java.util.Collections. Ces méthodes prennent une collection (ensemble, liste, dictionnaire) en paramètre et retournent une collection du même type dont les méthodes sont synchronisées : c'est-à-dire qu'il est possible de poser des sémaphores sur ces méthodes lors d’accès à la collection par des processus (threads) concurrents. La nouvelle collection empaquète la collection d'origine ; il n'y a donc pas de duplication de données ou de structure de stockage.

  • synchronizedCollection() - permet de créer une collection synchronisée (qui répond à l'interface java.util.Collection) ;
  • synchronizedList() - permet de créer une liste synchronisée ;
  • synchronizedMap() - permet de créer un dictionnaire synchronisé ;
  • synchronizedNavigableMap() - permet de créer un dictionnaire navigable synchronisée ;
  • synchronizedNavigableSet() - permet de créer un ensemble navigable synchronisée ;
  • synchronizedSet() - permet de créer un ensemble synchronisé ;
  • synchronizedSortedMap() - permet de créer un dictionnaire ordonné synchronisé ;
  • synchronizedSortedSet() - Permet de créer un ensemble ordonné synchronisé.


Par exemple :

Code Java : Sélectionner tout
1
2
List<String> maListe = Arrays.asList("Toto", "Titi", "Tata"); 
List<String> maListeSynchronisee = Collections.synchronizedList(maListe);

Ici, maListeSynchronisee dispose d’accès synchronisés sur ses méthodes. La liste maListe est contenue dans maListeSynchronisee. Si des modifications (retrait, ajout, changement de valeur) sont opérées sur maListe, elles seront répercutées sur maListeSynchronisee. Il faut cependant éviter d'effectuer de telles modifications sur la liste source puisque cette dernière ne dispose pas d’accès synchronisés.

Les accès sur le contenu de la liste doivent être effectués dans des blocs synchronisés sur la liste synchronisée elle-même :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Il faut synchroniser les accès en lecture. 
synchronized (maListeSynchronisee) { 
    String value = maListeSynchronisee.get(0); 
} 
// Et en écriture. 
synchronized (maListeSynchronisee) { 
    maListeSynchronisee.add("Tutu"); 
} 
// De même que les itérateurs. 
synchronized (maListeSynchronisee) { 
    Iterator i = maListeSynchronisee t.iterator(); 
    while (i.hasNext()) { 
        faireQuelqueChose(i.next()); 
    } 
} 
// Ou les flux. 
synchronized (maListeSynchronisee) { 
    maListeSynchronisee.stream() 
        .forEach(System.out::println); 
} 
// Ou toute opération qui touche au contenu de la liste. 
synchronized (maListeSynchronisee) { 
    Collections.sort(maListeSynchronisee); 
}

Attention : c'est la collection elle-même qui est synchronisée, et non pas les éléments qu'elle contient. Les accès aux méthodes, getters ou setters des éléments de la liste ne sont pas du tout synchronisés !

Mis à jour le 21 juin 2015 bouye

Comme nous l'avons indiqué plus haut, il est déconseillé d'utiliser la classe Vector. En effet, celle-ci fait partie des vieilles classes de Java 1.0 qui datent d'avant l'ajout du framework de collections. Ses méthodes ne respectent donc pas les standards de nommage des collections. De plus, cette classe est synchronisée, c'est-à-dire qu'elle peut s'utiliser dans un environnement multithreadé mais cela résulte en une perte de performances.

Il vaut donc mieux utiliser la classe ArrayList qui s'utilise presque de la même manière que Vector, mais qui n'est pas synchronisée et fait partie des nouvelles classes du framework. Si vous avez tout de même besoin d'une collection synchronisée du type de Vector, il vous suffit d'invoquer la méthode synchronizedList() de la classe Collections :

Code java : Sélectionner tout
1
2
3
List<Voiture> listeVoiture = new ArrayList<>([...]);  
[...] 
listeVoiture  = Collections.synchronizedList(listeVoiture);

Vous aurez ainsi une List tout à fait normale mais disposant d’accès synchronisés.

Mis à jour le 25 août 2007 bentechno Glob

Comme nous l'avons indiqué plus haut, il est déconseillé d'utiliser la classe Hashtable. En effet, celle-ci fait partie des vieilles classes de Java 1.0 qui datent d'avant l'ajout du framework de collections. Ses méthodes ne respectent donc pas les standards de nommage des collections. De plus, cette classe est synchronisée, c'est-à-dire qu'elle peut s'utiliser dans un environnement multithreadé, mais cela résulte en une perte de performances.

De plus, il n'est pas possible de stocker une valeur null dans ce type de dictionnaire : invoquer la méthode put() avec une valeur null génèrera une exception de type java.lang.NullPointerException.

Code Java : Sélectionner tout
1
2
3
4
5
6
Map<String, Object> map = new Hashtable<>();  
[...]  
map.put("toto", null); // Génère une NullPointerException.  
// Préférer :  
map.remove("toto");  
Object valeur = map.get("toto"); // Valeur est à null.

Il vaut donc mieux utiliser la classe HashMap qui s'utilise presque de la même manière que Hashtable, mais qui n'est pas synchronisée et fait partie des nouvelles classes du framework. Si vous avez tout de même besoin d'une collection synchronisée du type de Hashtable, il vous suffit d'invoquer la méthode synchronizedMap() de la classe Collections :

Code Java : Sélectionner tout
1
2
3
Map<Integer, Voiture> dictionnaireVoiture = new HashMap<>();   
[...] 
dictionnaireVoiture = Collections.synchronizedMap(dictionnaireVoiture);

Vous aurez ainsi une Map tout à fait normale, mais disposant d’accès synchronisés.

Mis à jour le 15 juin 2015 bouye

Il existe plusieurs manières de créer une pile (LIFO ou Last In - First Out ou « dernier entré - premier sorti ») :

Stack
La classe java.util.Stack pourrait correspondre à nos besoins, mais elle hérite de Vector ; ces méthodes sont donc synchronisées et plus lentes que si l'on créait une pile en se basant sur une collection de l'API Java 2 :

  • Écriture sur la pile : push() ;
  • Retrait de la pile : pop() ;
  • Lecture de la pile : peek().

Ici, le sommet de la pile où sont ajoutés et retirés les éléments est situé au niveau de la fin du vecteur.

Deque
L'interface java.util.Deque introduite dans le JDK 6 peut être utilisée pour manipuler une pile via ses méthodes :
  • Écriture sur la pile : addFirst() et offerFirst() ;
  • Retrait de la pile : removeFirst() et pollFirst ;
  • Lecture de la pile : peekFirst() et getFirst().

Ici, le sommet de la pile où sont ajoutés et retirés les éléments est situé au niveau du début du deque.

Note : Le mot « deque » (prononcer comme le mot « deck ») est une contraction de « double ended queue » qui signifie « file d'attente à double extrémité ».

ArrayDeque
La classe java.util.ArrayDeque introduite dans le JDK 6 est une implémentation concrète de Deque qui utilise un tableau pour son stockage interne.

LinkedList
La classe java.util.LinkedList implémente Deque et contient donc toutes les méthodes nécessaires à l’utilisation d'une pile.

Créer sa propre classe
Le but n'est pas de réinventer la roue, mais juste d'utiliser une classe qui porte le nom Stack ou Pile pour plus de clarté dans le code source sans plomber les performances avec la classe Stack de Java 1. L'implémentation proposée ici est basée sur le modèle des collections de Java 2, elle utilise une interface Stack et une classe concrète LinkedStack.

Mis à jour le 15 juin 2015 bouye Clement Cunin

Il existe plusieurs manières de créer une file (FIFO ou First In - First Out ou « premier entré - premier sorti »)  :

Queue
L'interface java.util.Queue introduite dans le JDK 5 peut être utilisée pour manipuler une file via ses méthodes :

  • Écriture en fin de file : add() et offer() ;
  • Retrait en tête de file : remove() et poll() ;
  • Lecture en tête de file  : element() et peek().


Deque
L'interface java.util.Deque introduite dans le JDK 6 peut être utilisée pour manipuler une file via ses méthodes :
  • Écriture en fin de file : addLast() et offerLast() ;
  • Retrait en tête de file : removeFirst() et pollFirst() ;
  • Lecture en tête de file : getFirst() et peekFirst().

L'interface Deque étend également l'interface Queue, il est donc possible d'utiliser les méthodes définies dans l'interface mère. Ici, l’avant de la file où sont retirés les éléments est situé au niveau du début du deque tandis que l’arrière de la file où sont ajoutés les éléments est situé en fin de deque.

Note : le mot « deque » (prononcer comme le mot « deck ») est une contraction de « double ended queue » qui signifie « file d'attente à double extrémité ».

ArrayDeque
La classe java.util.ArrayDeque introduite dans le JDK 6 est une implémentation concrète de Deque qui utilise un tableau pour son stockage interne.

LinkedList
La classe java.util.LinkedList implémente Deque et contient donc toutes les méthodes nécessaires à l’utilisation d'une file.

Mis à jour le 1er septembre 2015 bouye

Pour trier le contenu d'une liste, il suffit d'invoquer une des variantes de la méthode sort() de la classe utilitaire java.util.Collections :

Code Java : Sélectionner tout
1
2
3
4
5
6
List<Object> maListe = new ArrayList<>(); 
[...] 
Collections.sort(maListe); 
// Ou :  
Comparator<Object> unComparateur = [...] // Permet de comparer les objets de la liste entre eux. 
Collections.sort(maListe, unComparateur);

Le contenu de maListe est ainsi triée par ordre croissant. Pour trier dans l'ordre décroissant, il suffit de faire ceci :

Code Java : Sélectionner tout
Collections.sort(maListe, Collections.reverseOrder());

Attention : pour pouvoir invoquer la variante de la méthode sort() qui ne prend pas d'instance de Comparator en argument, il faut que les éléments dans la liste soient de type Comparable, tel que Integer, String, Date, etc. Prenons un exemple simple avec une classe personnalisée :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Voiture {  
    String marque = "";  
    int nbChevaux = 0;  
  
    public Voiture(String s, int i) {  
        marque = s;  
        nbChevaux = i;  
    }  
  
    public int getNbChevaux()  {  
        return nbChevaux;   
    } 
  
    public String toString()  {  
        return marque + "\t" + nbChevaux;    
    } 
}

On veut pouvoir trier une liste de Voiture suivant leur nombre de chevaux. Voilà alors les modifications à apporter à la classe :

Code java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Voiture implements java.lang.Comparable {  
  
   [...] 
  
    // méthode à implémenter 
    /** 
     * @param other Doit être de type Voiture ! 
     */ 
    public int compareTo(Object other) {  
        int nombre1 = ((Voiture) other).getNbChevaux();  
        int nombre2 = this.getNbChevaux();  
        return nombre2 - nombre1;  
   }  
}

Maintenant, vous pouvez trier facilement une liste de voitures :

Code java : Sélectionner tout
Collections.sort(listeVoiture);

Vous avez aussi la possibilité de facilement mélanger le contenu d'une liste en invoquant la méthode shuffle() de la classe java.util.Collections.

Mis à jour le 19 juillet 2004 bouye Braim duj Greg01

Il existe plusieurs manières de parcourir tous les éléments d'un ensemble :

Itérateur
On peut parcourir une collection de type Set au moyen d'un itérateur. Il suffira alors d'invoquer la méthode iterator() de la collection et de le parcourir avec une boucle for traditionnelle :

Code Java : Sélectionner tout
1
2
3
4
5
6
Set<Voiture> ensembleVoiture = [...]  
[...] 
for (Iterator<Voiture> i = ensembleVoiture.iterator() ; i.hasNext() ; ) {     
    Voiture value = i.next(); 
    System.out.println(value);  
}

Ou une boucle while :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
Set<Voiture> ensembleVoiture  = [...]  
[...] 
Iterator<Voiture> i = ensembleVoiture.iterator(); 
while (i.hasNext()) {  
    Voiture value = i.next(); 
    System.out.println(value);  
}

Boucle for étendue
Depuis le JDK 5.0, on peut aussi utiliser la boucle for étendue sur la collection.

Code Java : Sélectionner tout
1
2
3
4
5
Set<Voiture> ensembleVoiture = [...]  
[...] 
for (Voiture value : ensembleVoiture) { 
    System.out.println(value);  
}

Streams
Le JDK 8 permet d'utiliser des flux sur les collections. Par exemple :

Code Java : Sélectionner tout
1
2
3
4
Set<Voiture> ensembleVoiture = [...]  
[...] 
ensembleVoiture.stream() 
    .forEach(System.out::println);

Mis à jour le 15 juin 2015 bouye

Il existe deux manières simples de convertir une liste en tableau.

List.toArray()
Cette première méthode, qui ne prend pas de paramètre, permet de convertir une liste en un tableau d'objet (Object[]).

Code Java : Sélectionner tout
1
2
List<String> liste = Arrays.asList("Toto", "Tata", "Titi"); 
Object[] tableau = liste.toArray();

List.toArray(tableau)
Cette seconde méthode prend en paramètre un tableau préalloué du même type objet (ou d'un de ses types parents) que les éléments contenus dans la liste. Si le tableau fourni en paramètre n'a pas la capacité suffisante pour stocker tous les éléments de la liste, un nouveau tableau sera alloué et retourné par la méthode. Ce paramètre ne peut pas avoir la valeur null sous peine de générer une NullPointerException.

Code Java : Sélectionner tout
1
2
3
4
List<String> liste = Arrays.asList("Toto", "Tata", "Titi"); 
String[] tableau = liste.toArray(new String[0]); // Alloue un nouveau tableau pour le résultat. 
String[] tableau = liste.toArray(new String[10]); // Conserve le tableau passe en paramètre car la capacité est suffisante. 
String[] tableau = liste.toArray(null); // Lève une exception de type NullPointerException.

Note : le tableau obtenu est totalement indépendant de la liste source et tout changement sur la liste ou le tableau ne sera pas répercuté sur l'autre objet.

Mis à jour le 22 juin 2015 bouye

La conversion d'un ensemble (Set) vers un tableau se fait en utilisant la méthode toArray.

Code : Sélectionner tout
1
2
3
4
5
 
Set<String> sourceSet = new HashSet<String>(); 
sourceSet.add("message1"); 
sourceSet.add("message2"); 
String[] targetArray = sourceSet.toArray(new String[sourceSet.size()]);

Mis à jour le 8 mars 2017 Mickael Baron

Les moyens les plus simples de convertir un tableau en liste sont soit de créer une nouvelle ArrayList à partir du tableau source, soit d'invoquer la méthode toList() de la classe java.util.Arrays (qui effectue la même chose).

Code Java : Sélectionner tout
1
2
3
4
String[] tableau = { "Toto", "Tata", "Titi" }; 
List<String> liste = new ArrayList<>(tableau); 
// Ou  
List<String> liste = Arrays.asList(tableau); // Fait exactement la même chose.

Note : initialement, le tableau source sert de tableau de stockage pour la liste ; donc tout changement dans le tableau source impacte également la liste. Si la capacité de stockage de la liste change, alors le tableau de stockage de la liste sera réalloué et deviendra donc dissocié du tableau source.

Avertissement : il n'est bien sûr pas possible de convertir un tableau de literals de cette manière.

Mis à jour le 22 juin 2015 bouye

Deux façons sont disponibles pour convertir un tableau via un ensemble (Set).

Lors de la construction du Set

Code : Sélectionner tout
1
2
3
 
String[] sourceArray = { "message1", "message2", "message3", "message4" }; 
Set<String> targetSet = new HashSet<String>(Arrays.asList(sourceArray));

En créant d'abord un Set et en remplissant son contenu
Code : Sélectionner tout
1
2
3
4
 
String[] sourceArray = { "message1", "message2", "message3", "message4" }; 
Set<string> targetSet = new HashSet<String>(); 
Collections.addAll(targetSet, sourceArray);

Mis à jour le 8 mars 2017 Mickael Baron

Il existe plusieurs manières de parcourir tous les éléments d'un dictionnaire.

Itérateur
On peut parcourir une collection de type Map au moyen d'itérateurs. Pour cela, il suffit de récupérer soit les couples (clé, valeur), soit les clés, soit les valeurs contenues dans le dictionnaire avec, respectivement, les méthodes entrySet(), keySet() et values(). Il suffira alors d'invoquer la méthode iterator() de la collection obtenue et de le parcourir avec une boucle for traditionnelle :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Map<Integer, Voiture> dictionnaireVoiture  = [...]  
[...] 
// Parcours sur les couples de la table.  
for (Iterator<Map.Entry<Integer, Voiture>> i = dictionnaireVoiture.entrySet().iterator() ; i.hasNext() ; ) { 
    Map.Entry<Integer, Voiture> entry = i.next(); 
    System.out.println(entry);  
} 
// Parcours sur les clés de la table. 
for (Iterator<Integer> i = dictionnaireVoiture.keySet().iterator() ; i.hasNext() ; ) { 
    Integer key = i.next(); 
    System.out.println(key);  
} 
// Parcours sur les valeurs de la table. 
for (Iterator<Voiture> i = dictionnaireVoiture.values().iterator() ; i.hasNext() ; ) {  
    Voiture value = i.next(); 
    System.out.println(value);  
}

Ou une boucle while :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Map<Integer, Voiture> dictionnaireVoiture  = [...]  
[...] 
// Parcours sur les couples de la table.  
Iterator<Map.Entry<Integer, Voiture>> i = dictionnaireVoiture.entrySet().iterator(); 
while (i.hasNext()) { 
    Map.Entry<Integer, Voiture> entry = i.next(); 
    System.out.println(entry);  
} 
// Parcours sur les clés de la table. 
Iterator<Integer> i = dictionnaireVoiture.keySet().iterator(); 
while (i.hasNext()) { 
    Integer key = i.next(); 
    System.out.println(key);  
} 
// Parcours sur les valeurs de la table. 
Iterator<Voiture> i = dictionnaireVoiture.values().iterator(); 
while (i.hasNext()) {  
    Voiture value = i.next(); 
    System.out.println(value);  
}

Il est plus efficace de boucler sur les couples contenus dans la table que de parcourir les clés pour ensuite retirer les valeurs :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
// Mieux vaut faire : 
for (Iterator<Map.Entry<Integer, Voiture>> i = dictionnaireVoiture.entrySet().iterator() ; i.hasNext() ; ) { 
    Map.Entry<Integer, Voiture> entry = i.next(); 
    Integer key = entry.getKey(); 
    Voiture value = entry.getValue(); 
    System.out.println("key = " + key + " value = " + value);  
} 
// Que : 
for (Iterator<Integer> i = dictionnaireVoiture.keySet().iterator() ; i.hasNext() ; ) { 
    Integer key = i.next(); 
    Voiture value = dictionnaireVoiture.get(key); // Pas performant ! 
    System.out.println("key = " + key + " value = " + value);  
}

Boucle for étendue
Depuis le JDK 5.0, on peut aussi utiliser la boucle for étendue sur la collection listant les couples, les clés ou les valeurs pour parcourir une Map.

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Map<Integer, Voiture> dictionnaireVoiture  = [...]  
[...] 
// Parcours sur les couples de la table.  
for (Map.Entry<Integer, Voiture> entry : dictionnaireVoiture.entrySet()) { 
    System.out.println(entry);  
} 
// Parcours sur les clés de la table. 
for (Integer key : dictionnaireVoiture.keySet()) { 
    System.out.println(key);  
} 
// Parcours sur les valeurs de la table. 
for (Voiture value : dictionnaireVoiture.values()) { 
    System.out.println(value);  
}

Streams
Le JDK 8 permet d'utiliser des flux sur les collections permettant d’accéder aux couples, aux clés ou aux valeurs du dictionnaire. Par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Map<Integer, Voiture> dictionnaireVoiture  = [...]  
[...] 
// Parcours sur les couples de la table.  
dictionnaireVoiture.entrySet() 
    .stream() 
    .forEach(System.out::println); 
// Parcours sur les clés de la table. 
dictionnaireVoiture.keySet() 
    .stream() 
    .forEach(System.out::println); 
// Parcours sur les valeurs de la table. 
dictionnaireVoiture.values() 
    .stream() 
    .forEach(System.out::println);

Mis à jour le 25 août 2007 bentechno le y@m's osopardo

Il existe plusieurs manières de parcourir tous les éléments d'une liste :

Boucle for ou while
Nous pouvons effectuer le parcours à l'ancienne, avec une boucle for traditionnelle, par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
6
List<Voiture> listeVoiture = [...]  
[...] 
for (int index = 0 ; index < listeVoiture.size() ; index++) {     
    Voiture value = listeVoiture.get(index); // Pas performant ! 
    System.out.println(value);  
}

Ou une boucle while :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
List<Voiture> listeVoiture = [...]  
[...] 
while (index < listeVoiture.size()) {     
    Voiture value = listeVoiture.get(index); // Pas performant ! 
    System.out.println(value);   
    index++; 
}

Cependant, dans les deux cas, le temps d'un accès direct à la valeur contenue dans la liste par son index dépend fortement de l’implémentation sous-jacente de la liste. Il peut être ainsi très long dans le cas d'une liste chaînée. On préférera passer par un itérateur dans la mesure du possible.

Itérateur
On peut parcourir une collection de type List au moyen d'un itérateur. Il suffira alors d'invoquer la méthode iterator() de la collection et de le parcourir avec une boucle for traditionnelle :

Code Java : Sélectionner tout
1
2
3
4
5
6
List<Voiture> listeVoiture = [...]  
[...] 
for (Iterator<Voiture> i = listeVoiture.iterator() ; i.hasNext() ; ) {     
    Voiture value = i.next(); 
    System.out.println(value);  
}

Ou une boucle while :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
List<Voiture> listeVoiture = [...]  
[...] 
Iterator<Voiture> i = listeVoiture.iterator(); 
while (i.hasNext()) {  
    Voiture value = i.next(); 
    System.out.println(value);  
}

Boucle for étendue
Depuis le JDK 5.0, on peut aussi utiliser la boucle for étendue sur la collection.

Code Java : Sélectionner tout
1
2
3
4
5
List<Voiture> listeVoiture = [...]  
[...] 
for (Voiture value : listeVoiture) { 
    System.out.println(value);  
}

Streams
Le JDK 8 permet d'utiliser des flux sur les collections. Par exemple :

Code Java : Sélectionner tout
1
2
3
4
List<Voiture> listeVoiture = [...]  
[...] 
listeVoiture.stream() 
    .forEach(System.out::println);

Mis à jour le 15 juin 2015 bouye

Dans un dictionnaire, il n'y a pas de notion d'ordre vu que l'on n'accède généralement pas à ses éléments par indice comme dans une liste, mais plutôt par des clés. Trier un objet de type Map suivant les valeurs revient en fait à :

  • soit copier la liste des valeurs du Map dans une liste ou un tableau et trier ce conteneur. L'inconvénient est que l'on ne travaille plus sur le dictionnaire et donc on n'a plus un accès direct aux éléments par clés ;
  • soit récupérer l'ensemble des clés du dictionnaire dans une liste, la trier suivant les valeurs et accéder aux éléments du dictionnaire parcourant séquentiellement les éléments de cette collection. Cette manière de faire n'est pas vraiment optimisée compte tenu du nombre d’accès nécessaires pour récupérer les valeurs lors des tests ;
  • ou encore récupérer l'ensemble des couples du dictionnaire dans une liste, la trier suivant les valeurs et accéder aux valeurs en parcourant séquentiellement les couples contenus dans cette collection. L'avantage est qu'on accède toujours aux éléments directement par le couple contenu dans l'association. C'est à cette méthode que l'on s'intéressera.


Exemple
Considérons la classe suivante :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
 public class Personne { 
    private Long id; // Identifiant d'une personne. 
    private int age; 
    private String nom; 
  
    // Implémenter les getters et les setters. 
   [...] 
}

Et supposons que l'on stocke un ensemble de personnes dans un Map avec l'identifiant comme clé.

Code Java : Sélectionner tout
Map<Long, Personne> personnes;

Pour trier ce dictionnaire en fonction de l'âge des personnes par exemple, on procède comme suit :

  1. Commençons par créer un Comparator qui permet de comparer deux entrées du dictionnaire suivant l'âge des personnes qu'elles contiennent.

    Code Java : Sélectionner tout
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class PersonneComparator implements Comparator<Map.Entry<Long, Personne>> { 
      
        public PersonneComparator() { 
        } 
      
        @Override 
        public int compare(Map.Entry<Long, Personne> entry1, Map.Entry<Long, Personne> entry2) { 
            Personne p1 = entry1.getValue(); 
            Personne p2 = entry2.getValue();  
            // Comparer les deux couples en fonction de l'âge des personnes contenues. 
            return p1.getAge() - p2.getAge(); 
        } 
    }
  2. Trier les clés du dictionnaire en utilisant ce Comparator :

    Code Java : Sélectionner tout
    1
    2
    List<Map.Entry<Long, Personne>> entries = new ArrayList<>(personnes.entrySet()); 
    Collections.sort(entries, new PersonneComparator());
  3. Accéder aux éléments du dictionnaire en utilisant la liste triée :

    Code Java : Sélectionner tout
    1
    2
    3
    4
    5
    for (Map.Entry<Long, Personne> entry : entries){ 
        Long cle = entry.getKey(); 
        Personne personne = entry.getValue(); 
        [...] 
    }


Le JDK 8 permet même de simplifier les choses en faisant :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
personnes.entrySet() 
    .stream() 
    .sorted(new PersonneComparator()) 
    .forEach(entry -> { 
        Long cle = entry.getKey(); 
        Personne personne = entry.getValue();   
        [...]       
    });

Mis à jour le 15 juin 2015 bouye djo.mos

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

 
Responsables bénévoles de la rubrique Java : Mickael Baron - Robin56 -