• 6 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Mis à jour le 30/11/2016

L'interface Map<K, V>

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Ce type d'objets gère ses données avec un système de clé-valeur.
Elle diverge donc franchement des autres collections vues précédemment, et c'est pourquoi ses interfaces et ses objets n'héritent pas de l'objet Collection<E>.

Je ne vais pas vous faire une introduction magistrale sur ces objets vu que nous allons tout de suite voir ce qui les caractérise !
En route moussaillons ! :pirate:

Généralités

Comme je vous le disais dans l'introduction, les collections de type Map<K,V> stockent leurs contenus sous la forme clé-valeur : on pourrait dire qu'il s'agit d'une collection de paires. Ceci signifie donc qu'il ne peut pas y avoir deux fois la même clé dans ce genre de collection. Par contre, la même valeur peut-être présente dans deux clés différentes. L'avantage de ce genre d'objets est qu’ils permettent de retrouver rapidement une valeur en fonction de leur clé.

Ces objets sont donc tout indiqués pour gérer des annuaires, des dictionnaires etc.

Comme à l'accoutumée, voici un petit topo sur les grandes fonctionnalités de ce type d'objets. Je vous propose de voir tout de suite le diagramme de classes concernant cette l'interface Map<K,V> :

Image utilisateur

Nous avons beaucoup de choses à voir avec cette partie du framework Collections. Nous avons donc des implémentations de base, héritant de l'objet AbstractMap<K,V>, et des interfaces qui ajoutent, encore une fois, des restrictions ou des fonctionnalités à ce type d'objets. Commençons par détailler le contenu de l'interface Map<K,V> :

Méthode

Définition

containsKey(Object key)

Retourne true si l'objet passé en paramètre correspond à une clé de la collection.

containsValue(Object value)

Idem que ci-dessus mais sur une valeur cette fois.

entrySet()

Retourne un objet de type Set<Map.Entry<K,V> représentant le contenu de la collection. Comme vu dans le premier chapitre de cette partie, ce type d'objet nous donne une vue du contenu de la collection et est tout indiqué pour la parcourir lorsque vous avez besoin des clés et des valeurs.

get(Object key)

Retourne la valeur associée à la clé passée en paramètre ou null si cette clé n'existe pas.

keySet()

Retourne un objet de type Set<K> représentant la liste des clés contenues dans la collection.

put(K key, V value)

Ajoute la clé et la valeur dans la collection en retournant la valeur insérée. Si la clé existe déjà, sa valeur sera écrasée par celle passée en paramètre de la méthode.

putAll(Map<? extends K,? extends V> m)

Ajoute le contenu de la collection en paramètre dans la collection appelante.

remove(Object key)

Supprime le couple clé-valeur associé à la clé passée en paramètre et retourne la valeur supprimée.

values()

Retourne une objet de type Collection<V> contenant toutes les valeurs de la collection.

Le langage vous propose trois implémentations de bases :

  • HashMap<K,V> : implémentation utilisant une table de hachage pour stocker ses éléments, mais cet objet n'est pas thread-safe ;

  • TreeMap<K,V> : implémentation qui stocke les éléments triés, de façon naturelle par défaut, mais utilisable avec un comparateur ;

  • LinkedHashMap<K,V> : implémentation qui combine table de hachage et liens chaînés pour stocker ses éléments, ce qui facilite leur insertion et leur suppression.

Voici pour la présentation des actions communes à toutes les implémentations. Je vous propose maintenant de passer toutes celles-ci en revues !

Les implémentations de Map<K,V>

Comme vous avez pu le voir dans le diagramme de classes vu précédemment, il y a cinq objets qui héritent de la classe AbstractMap<K,V>, elle-même implémentant directement l'interface Map<K,V>, sans autres interfaces supplémentaires. Beaucoup de ces objets travaillent sous forme de table de hachage, ce qui est logique vu qu'ils contiennent "Hash" dans leur nom... :-°

Ces objets représentent donc une forme de base de cette interface et nous allons maintenant les voir plus en détail.

Les objets HashMap<K,V> et LinkedHashMap<K,V>

Comme dit précédemment, ces deux objets sont deux des plus utilisés. Ils travaillent sous forme de table de hachage et leur principale différence réside dans le fait qu'un des deux gère en plus des liens chaînés (vous avez deviné duquel je parle...), ce qui fait que ce dernier permet de parcourir les éléments de la collection dans l'ordre d'insertion alors que le second ne le permet pas...

La valeur null est autorisée comme clé ou comme valeur.

Voici un code d'exemple utilisant ces deux objets :

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


public class Main {
   public static void main(String[] args) {

      //La fameuse syntaxe en diamant de Java 7
      Map<Integer, String> hm = new HashMap<>();
      hm.put(10, "1");
      hm.put(20, "2");
      hm.put(30, "3");
      hm.put(40, "4");
      hm.put(50, "5");
      //Ceci va écraser la valeur 5
      hm.put(50, "6");
      
      System.out.println("Parcours de l'objet HashMap : ");
      Set<Entry<Integer, String>> setHm = hm.entrySet();
      Iterator<Entry<Integer, String>> it = setHm.iterator();
      while(it.hasNext()){
         Entry<Integer, String> e = it.next();
         System.out.println(e.getKey() + " : " + e.getValue());
      }
      
      System.out.println("Valeur pour la clé 8 : " + hm.get(8));
      
      Map<Integer, String> lhm = new LinkedHashMap<>();
      lhm.put(10, "1");
      lhm.put(20, "2");
      lhm.put(30, "3");
      lhm.put(40, "4");
      lhm.put(50, "5");
      
      System.out.println("Parcours de l'objet LinkedHashMap : ");      
      Set<Entry<Integer, String>> setLhm = lhm.entrySet();
      Iterator<Entry<Integer, String>> it2 = setLhm.iterator();
      while(it2.hasNext()){
         Entry<Integer, String> e = it2.next();
         System.out.println(e.getKey() + " : " + e.getValue());
      }
   }
}

Et le résultat :

Parcours de l'objet HashMap : 
50 : 6
20 : 2
40 : 4
10 : 1
30 : 3
Valeur pour la clé 8 : null
Parcours de l'objet LinkedHashMap : 
10 : 1
20 : 2
30 : 3
40 : 4
50 : 5

L'objet WeakHashMap<K,V>

Cet objet embarque un mode de fonctionnement un peu particulier car les références contenues seront automatiquement supprimées dès lors que les clés utilisées sont considérées comme caduque — et par caduque je veux dire inaccessible car éliminée de la mémoire.

La valeur null est autorisée comme clé ou comme valeur.

Vous n'êtes pas sans savoir que Java utilise un Garbage Collector (ramasse-miettes) pour gérer sa mémoire et ainsi supprimer de celle-ci les objets qui ne servent plus à rien. Cette collection supprime automatiquement les entrées référencées par un objet qui est détruit par le ramasse-miettes.

Un exemple sera plus parlant :

import java.util.WeakHashMap;


public class Main2 {

   public static void main(String[] args) {
        
     WeakHashMap<MyKey, String> whm = new WeakHashMap<>();
     MyKey key1 = new MyKey(1), key2 = new MyKey(2), key3 = new MyKey(3);
     whm.put(key1, "Valeur de toto");
     whm.put(key2, "Valeur de titi");
     whm.put(key3, "Valeur de tutu");
     
     //On affiche la collection
     System.out.println(whm);
     
     key1 = null;
     //On force le passage du ramasse-miettes
     //ce qui va détruire l'objet en mémoire 
     //et donc dans la collection
     System.gc();
     System.out.println("\nAprès la destruction de la clé 1 par le ramasse-miettes : ");
     System.out.println(whm);
     
     key3 = null;
     System.gc();
     System.out.println("\nAprès la destruction de la clé 3 par le ramasse-miettes : ");
     System.out.println(whm);
   }
}

/**
 * Cette objet sert uniquement comme exemple
 * Il contient un simple entier
 */
class MyKey{
   private int key = 0;
   public MyKey(int key){
      this.key = key;
   }
   public String toString(){
      return "[MyKey:" + key + "]";
   }
}

Et le résultat :

{[MyKey:1]=Valeur de toto, [MyKey:2]=Valeur de titi, [MyKey:3]=Valeur de tutu}

Après la destruction de la clé 1 par le ramasse-miettes : 
{[MyKey:2]=Valeur de titi, [MyKey:3]=Valeur de tutu}

Après la destruction de la clé 3 par le ramasse-miettes : 
{[MyKey:2]=Valeur de titi}

Vous pouvez voir qu'après la destruction de nos clés, la collection se vide d'elle-même... :magicien:

L'objet IdentityHashMap<K,V>

Cet objet est aussi particulier dans le sens où il viole la contrainte de base des objets de types Map<K,V> car ce dernier n'utilise pas un système d'égalité par objet mais par référence.
Je m'explique. D'ordinaire, ce genre de collection utilise l'égalité via la méthode equals(), mais cet objet utilise l'égalité par référence avec ==.
La valeur null est autorisée comme clé ou comme valeur.

Pour bien comprendre, rien de tel qu'un bon exemple :

import java.util.HashMap;
import java.util.IdentityHashMap;

public class Main3 {
   public static void main(String[] args) {
      Integer i1 = new Integer(1);
      Integer i2 = new Integer(1);
      
      HashMap<Integer, String> hm = new HashMap<>();
      hm.put(i1, "toto");
      hm.put(i2, "titi");
      //Ici, vu que i1.equals(i2) == true
      //nous n'aurons qu'un seule entrée dans la collection
      System.out.println(hm);
      //Ces trois instructions renverront la même valeur
      System.out.println(hm.get(i1));
      System.out.println(hm.get(i2));
      System.out.println(hm.get(1));
      
      System.out.println("----------------------------------");
      System.out.println("Maintenant avec IdentityHashMap : ");
      System.out.println("----------------------------------");
      IdentityHashMap<Integer, String> ihm = new IdentityHashMap<>();
      ihm.put(i1, "toto");
      ihm.put(i2, "titi");
      //Ici, vu que i1 == i2 => false
      //nous aurons deux entrées dans la collection
      System.out.println(ihm);
      //Ces trois instructions seront différentes
      System.out.println(ihm.get(i1));
      System.out.println(ihm.get(i2));
      System.out.println(ihm.get(1));
   }
}

Ce qui nous donne :

{1=titi}
titi
titi
titi
----------------------------------
Maintenant avec IdentityHashMap : 
----------------------------------
{1=toto, 1=titi}
toto
titi
null

Vous devez mieux saisir le caractère sensible de ce genre de collections...

L'objet EnumMap<K,V>

Cet objet se distingue par sa gestion des clés qui la constituent. Toutes les clés doivent provenir de l'énumération définie lors de l'instanciation de l'objet. De ce fait, la valeur null n'est pas autorisée comme clé.

Voici un exemple simple :

import java.util.EnumMap;


public class Main4 {

   public static void main(String[] args) {
        EnumMap<Days, String> em = new EnumMap<>(Days.class);
        em.put(Days.LUNDI, null);
        em.put(Days.MARDI, "Métro");
        em.put(Days.MERCREDI, "Boulot");
        em.put(Days.JEUDI, "Dodo");
        em.put(Days.VENDREDI, "Boulot et apéro !");
        em.put(Days.SAMEDI, "Dodo et apéro !");
        em.put(Days.DIMANCHE, "apéro ! apéro ! apéro !");
        
        //affichage
        System.out.println(em);
        
        //Parcours de la collection
        for(Days d : Days.values())
           System.out.println(em.get(d));
   }
}

Et le résultat :

{Lundi=null, Mardi=Métro, Mercredi=Boulot, Jeudi=Dodo, 
Vendredi=Boulot et apéro !, Samedi=Dodo et apéro !, Dimanche=apéro ! apéro ! apéro !}
null
Métro
Boulot
Dodo
Boulot et apéro !
Dodo et apéro !
apéro ! apéro ! apéro !

Les interfaces SortedMap<K,V> et NavigableMap<K,V>

Comme vous vous en doutez maintenant, ces deux interfaces ajoutent des comportements à notre interface de base. Ces deux interfaces n'ont qu'une implémentation directe, l'objet TreeMap<K,V>, ce chapitre lui sera donc consacré mais, avant cela, voyons un peu ce qu'il y a dans nos deux interfaces.

L'interface SortedMap<K,V>

Comme son nom l'indique, cette interface permet d'avoir une collection dont les éléments sont triés selon leur ordre naturel ou via un comparateur et ce tri sera directement visible lorsque vous parcourrez votre collection.

C'est un peu comme l'interface SortedSet<K> ?

Tout à fait. Vous avez compris. Cette interface joue le même rôle pour l'interface Map<K,V> que l'interface SortedSet<K> pour l'interface Set<K>.

Que les choses soient claires, le tri s'effectue sur les clés. Ce seront donc les objets utilisés comme clé qui devront implémenter l'interface Comparable<T> ! Sauf si vous utilisez un comparateur, bien entendu.

Voici un tableau qui liste les méthodes qu'offre cette interface :

Méthode

Définition

comparator()

Retourne le comparateur utilisé pour ranger les clé de la collection, ou null si celle-ci utilise l'ordre naturel des clés.

entrySet()

Nous l'avons vu dans le premier chapitre de cette partie. Cette méthode retourne un Set<Map.Entry<K,V>> représentant une vue des couple clé-valeur de la collection.

firstKey()

Retourne la première clé de la collection.

headMap(K toKey)

Retourne une partie de la collection allant du début jusqu'au paramètre, exclu.

lastKey()

Retourne la plus grande de la collection.

subMap(K fromKey, K toKey)

Retourne une sous-collection ou les clés sont comprisent entre le premier paramètre, inclus, et le deuxième paramètre, exclu.

tailMap(K fromKey)

Retourne une collection où les clés sont supérieures ou égales au paramètre.

values()

Retourne une collection contenant la liste des valeurs.

L'interface NavigableMap<K,V>

Cette interface rajoute toute une batterie de méthodes qui permettent la navigation dans votre collection. Elle permet aussi et surtout de pouvoir naviguer dans votre collection dans les deux sens mais gardez en tête que le parcours ascendant est un peu plus rapide que le parcours descendant.

Certaines méthodes, comme nous le verrons dans le tableau récapitulatif, sont surchargées et acceptent des arguments supplémentaires, notamment les méthodes qui retournent des sous-collections.

Allez, le tableau des méthodes de cette interface :

Méthode

Définition

ceilingEntry(K key)

Retourne un objet Map.Entry<K,V> correspondant à la plus petite clé supérieure ou égale au paramètre, ou null s'il n'y a pas de clé.

ceilingKey(K key)

Retourne la plus petite clé supérieure ou égale au paramètre, ou null s'il n'y a pas de clé.

descendingKeySet()

Retourne un objet NavigableSet<K> correspondant aux clés de la collection, mais dans l'ordre inverse de celui de la collection.

descendingMap()

Retourne un objet NavigableMap<K,V> contenant notre collection dans l'ordre inverse.

firstEntry()

Retourne un objet Map.Entry<K,V> représentant le couple clé-valeur de la plus petite clé de la collection, ou null s'il n'y a pas de clé.

floorEntry(K key)

Idem que ci-dessus, mais sur la clé la plus grande inférieure ou égale au paramètre

floorKey(K key)

Retourne la plus grande clé inférieure ou égale au paramètre, ou null s'il n'y a pas de clé.

headMap(K toKey, boolean inclusive)

Retourne un objet NavigableMap<K,V> qui contiendra tous les couples clé-valeur dont la clé est inférieure au paramètre (ou égale si le deuxième paramètre est à true).

higherEntry(K key)

Retourne un objet Map.Entry<K,V> représentant le couple clé-valeur de la plus petite clé de la collection strictement supérieure au paramètre, ou null s'il n'y a pas de clé.

higherKey(K key)

Idem que ci-dessus mais retourne uniquement la clé.

lastEntry()

Retourne un objet Map.Entry<K,V> représentant le couple clé-valeur de la plus grande clé de la collection, ou null s'il n'y a pas de clé.

lowerEntry(K key)

Idem que ci-dessus mais pour la plus petite clé de la collection.

lowerKey(K key)

Retourne la plus grande clé strictement plus petite que le paramètre.

navigableKeySet()

Retourne un objet NavigableSet<K> représentant une vue des clés de la collection.

pollFirstEntry()

Retourne un objet Map.Entry<K,V> correspondant à la plus petite clé, tout en supprimant cette entrée de la collection. Renvoie null si la clé n'existe pas.

pollLastEntry()

Idem que ci-dessus mais pour la plus grande clé de la collection.

subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)

Retourne une portion de la collection délimitée par les clés passées en paramètre et où les booléens servent à déterminer si les limites sont inclusives ou exclusives.

tailMap(K fromKey, boolean inclusive)

Retourne un objet NavigableMap<K,V> qui contiendra tous les couples clé-valeur dont la clé est strictement supérieure au paramètre (ou supérieure ou égale si le deuxième paramètre est à true).

Il y a beaucoup de méthodes, on est bien d'accord ! Et la plupart nous retourne soit une entrée de la collection soit une collection contenant tout ou parti de la collection d'origine. Maintenant que vous connaissez le contenu de ces deux interfaces voyons comment utiliser l'objet qui les implémente directement : l'objet TreeMap<K,V>.

L'objet TreeMap<K,V>

Comme vous vous en doutez déjà, cet objet est donc trié selon l'ordre naturel des clés le constituant, mais vous avez aussi la possibilité d'utiliser un comparateur si cet ordre ne vous convient pas. ;)
Par contre, gardez en tête que cet objet n'est pas synchronisé : il existe d'autres objets qu'il faut préférer utiliser en milieu multithread, nous les aborderons dans la prochaine section de ce chapitre.

Je vous propose un petit code d'exemple utilisant cet objet, vous allez voir, il est très simple d'utilisation :

import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;


public class Main5 {

   public static void main(String[] args) {
      
      TreeMap<Integer, String> tm = new TreeMap<>();
      tm.put(10, "10");
      tm.put(50, "50");
      tm.put(20, "20");
      tm.put(40, "40");
      tm.put(30, "30");
      //Cette instruction affiche le contenu trié de la collection
      System.out.println(tm);
      
      //Exemple de méthodes de l'interface SortedMap<K,V>
      //-----------------------------------------------------
      //Retourne les deux premiers éléments de la collection
      System.out.println(tm.headMap(22));
      //Retourne les trois derniers éléments de la collection
      System.out.println(tm.tailMap(22));
      //retourne la liste des valeurs
      System.out.println(tm.values());
      
      //Exemple de méthodes de l'interface NavigableMap<K,V>
      //-----------------------------------------------------
      //Retourne le couple correspondant à la clé 40
      System.out.println(tm.ceilingEntry(32));
      //Retourne la première entrée de la collection
      System.out.println(tm.firstEntry());
      //Retourne la clé 50
      System.out.println(tm.lowerKey(61));
      //Retourne les couples correspondant aux clés 30 et 40
      System.out.println(tm.subMap(20, false, 40, true));
      
      System.out.println("Nous allons parcourir cette collection");
      Set<Entry<Integer, String>> set = tm.entrySet();
      Iterator<Map.Entry<Integer, String>> it = set.iterator();

      //Nous créons une nouvelle collection avec un comparateur 
      //pour trier dans le sens inverse
      TreeMap<Integer, String> tm2 = new TreeMap<>(new Comparator<Integer>(){
         public int compare(Integer o1, Integer o2) {
            return o2.compareTo(o1);
         }
      });
      
      //On parcourt notre itérateur
      while(it.hasNext()){
         Map.Entry<Integer, String> entry = it.next();
         System.out.println(entry);
         //Et on insère dans notre nouvelle collection
         tm2.put(entry.getKey(), entry.getValue());
      }
      
      //La collection s'affiche bien dans le sens inverse de la précédente
      System.out.println(tm2);
      
      //une autre méthode pour parcourir une collection de ce type
      //mais cette fois, les éléments sont retirés au fur et à mesure...
      System.out.println("Dépilage de la deuxième collection :");
      Entry<Integer, String> entry = null;
      while((entry = tm2.pollFirstEntry()) != null)
         System.out.println(entry);
      
      //A ce moment du programme, il n'y a plus rien dans la seconde collection
      System.out.println(tm2);
   }
}

Et le résultat nous donne ceci :

{10=10, 20=20, 30=30, 40=40, 50=50}
{10=10, 20=20}
{30=30, 40=40, 50=50}
[10, 20, 30, 40, 50]
40=40
10=10
50
{30=30, 40=40}
Nous allons parcourir cette collection
10=10
20=20
30=30
40=40
50=50
{50=50, 40=40, 30=30, 20=20, 10=10}
Dépilage de la deuxième collection :
50=50
40=40
30=30
20=20
10=10
{}

Pratique, n'est-ce pas ? Je vous propose maintenant de voir les deux dernières interfaces de ce type de collection et leurs implémentations.
Vous verrez que ces dernières ressemblent beaucoup à leurs cousines... :-°

Les interfaces ConcurrentMap<K,V> et ConcurrentNavigableMap<K,V>

Comme je vous l'avais dit et comme vous vous en doutiez sûrement déjà, ces deux interfaces sont implémentées par des objets qui seront utilisables dans un environnement où la concurrence multithread sera rude...
Chacune de ces deux interfaces possède une implémentation héritant aussi de la classe AbstractMap<K,V>. Je vous propose de les passer en revue.

L'interface ConcurrentMap<K,V>

Celle-ci ajoute quelques méthodes à l'interface Map<K,V> :

Méthode

Définition

putIfAbsent(K key, V value)

Si la clé spécifiée en paramètre n'est pas déjà présente dans la collection, le couple clé/valeur sera inséré dans la collection. Cette méthode retourne la valeur correspondant à la clé passée, donc si la valeur existe déjà, ce sera la valeur présente dans la collection qui sera retournée et non la valeur en paramètre.

remove(Object key, Object value)

Méthode supprimant le couple clé/valeur passée en paramètre. Si ce couple n'est pas présent, aucune suppression n'est faite. Cette méthode retourne un booléen si la suppression est effective.

replace(K key, V value)

Fonctionne globalement comme la méthode ci-dessus mais ne supprime pas le couple clé/valeur, la méthode remplace la valeur de la clé et cette méthode ne retourne pas un booléen mais la valeur modifiée et donc null si la clé n'existe pas.

replace(K key, V oldValue, V newValue)

Comme la méthode ci-dessus mais contrôle l'existence du couple clé/valeur. Retourne un booléen représentant le résultat.

L'objet implémentant cette interface se nomme ConcurrentHashMap<K,V>. Cette implémentation autorise autant de lectures que vous le voulez en environnement multithread donc aucune exception de type ConcurrentModificationException ne sera levée.

Voyons tout de suite son utilisation.

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class Main6 {

   public static void main(String[] args) {
      //On crée un objet et on y met une entrée
      ConcurrentMap<Integer, String> concurent = new ConcurrentHashMap<>();
      concurent.putIfAbsent(10, "toto");
      
      //On récupère la valeur
      String str = concurent.putIfAbsent(10, "titi");
      //Affiche toto car 10 est déjà présent
      System.out.println(str);
      
      System.out.println("Test de suppression de valeur");
      System.out.println("-----------------------------");
      //On tente de supprimer
      boolean bool = concurent.remove(10, "titi");
      //Et ça ne fonctionne pas
      System.out.println(bool);
      
      //Ici, ça fonctionnera
      bool = concurent.remove(10, "toto");
      System.out.println(bool);
      
      System.out.println("Test de replacement de valeur");
      System.out.println("-----------------------------");
      //On rajoute une entrée
      concurent.putIfAbsent(10, "tutu");
      //On tente le remplacement
      System.out.println(concurent.replace(20, "tata"));
      System.out.println(concurent.replace(10, "tata"));
      
      System.out.println(concurent.replace(10, "tutu", "tyty"));
      System.out.println(concurent.replace(10, "tata", "tyty"));
      
   }
}

Et le résultat :

toto
Test de suppression de valeur
-----------------------------
false
true
Test de replacement de valeur
-----------------------------
null
tutu
false
true

L'interface ConcurrentNavigableMap<K,V>

Cette interface permet de lier l'interface ConcurrentNavigableMap<K,V> vue ci-dessus et l'interface NavigableMap<K,V>. Ceci permet donc d'avoir une collection qui a les propriétés de ces deux interfaces : l'utilisation en milieu multithread et toute une panoplie de méthodes qui permettent de naviguer dans votre collection.

Je ne vais pas détailler le contenu de cette interface car elle reprend la plupart des méthodes l'interface NavigableMap<K,V>, mais au lieu de retourner un objet NavigableMap<K,V>, elle retourne un objet ConcurrentNavigableMap<K,V>.

Une implémentation de cette interface est disponible : l'objet ConcurrentSkipListMap<K,V>. Ce dernier est une collection triée selon l'ordre naturel de ses clés (sauf si vous utilisez un comparateur), et n'accepte pas de valeur null.

Comme je vous ai déjà présenté le mode de fonctionnement de ces interfaces, je ne vais pas me répéter... je m'arrête donc là pour cet objet.

Encore un chapitre rondement mené. :-°
Voilà, vous venez de voir les derniers objets de ce framework Collections.
Ces derniers sont aussi très utiles et permettent de gérer certaines données plus simplement qu'avec une collection conventionnelle.

J'espère que ce chapitre vous aura appris plein de choses et que les Map<K,V> n'auront plus de secrets pour vous.

Bon, maintenant les collections ne devraient plus avoir de secrets pour vous.
Comme je vous le disais, cette partie n'était pas très difficile en soi, mais elle est tout de même importante, donc vous pouvez vous féliciter d'en être arrivés à bout. :) 

Et si vous voulez continuer votre voyage dans le monde de Java, n'hésitez pas à faire escale à mes cours suivants : 

Exemple de certificat de réussite
Exemple de certificat de réussite