Archives Wild Code School
Java Avancé

Collection 04 : Map

Quête 4 sur 15

Que ce soit avec des listes ou des ensembles, tu as pu rassembler des éléments, triés ou non. Dans le cas des listes, un index numérique est attribué automatiquement.

Mais que faire si tu désires attribuer toi-même un index aux éléments ? Et si cet index ne doit pas être pas numérique ?

Par exemple, un index pourrait être un pays, sa valeur associée étant sa capitale : pour réaliser cela, tu vas avoir besoin des tableaux associatifs (ou dictionnaires). En Java, cela est représenté par l'interface Map !

Objectifs

  • Savoir initialiser et utiliser un tableau associatif
  • Trier un tableau associatif par ordre d'insertion des clés
  • Trier un tableau associatif par valeur des clés

Map

Si nous reprenons l'exemple de l'introduction, un tableau associatif de pays avec leurs capitales respectives ressemble à cela :

json
{
    "Allemagne": "Berlin",
    "Autriche": "Vienne",
    "Belgique": "Bruxelles",
    "Bulgarie": "Sofia",
    "Chypre": "Nicosie",
    "Croatie": "Zagreb",
    "Danemark": "Copenhague",
    "Espagne": "Madrid",
    // ...
}

L'interface Map<K,V> est représentée par les types de la clé K et le type de la valeur V : dans l'exemple, il s'agit de Map<String, String>.

Un Map peut avoir n'importe quel type objet en clé, de même qu'en valeur. Il peut même y avoir une liste, un ensemble, voire même un autre tableau associatif !

Un tableau associatif est composé de trois zones distinctes :

  • l'ensemble de ses clés, keySet : deux clés ne peuvent être identiques

  • la collection de ses valeurs, values : il est possible d'avoir des valeurs identiques

  • l'ensemble des couples clé-valeur, entrySet : pas de doublons possibles

Il existe plusieurs implémentations de l'interface Map, notamment :

  • sans ordre défini, avec HashMap

  • par ordre d'insertion, avec LinkedHashMap

  • par ordre de valeur des clés, avec TreeMap

Au contraire de List et Set, l'interface Map n'hérite pas de Collection.

_Credit _Alla Gorik

↗
Oracle : Interface Map
docs.oracle.com

HashMap

Si l'ordre des clés importe peu, il est conseillé d'utiliser l'implémentation HashMap.

Pour créer un nouvel HashMap vide, tu vas devoir créer une instance de la classe et la stocker dans une référence :

java
Map<User, Computer> classroom = new HashMap<>();

Ici, nous avons la représentation d'une salle de classe, avec pour chaque utilisateur User un ordinateur associé Computer.

Les clés étant un ensemble, il n'est pas possible d'y avoir plusieurs fois le même utilisateur avec des ordinateurs différents. Cependant, il est envisageable que deux utilisateurs aient le même ordinateur (travail en peer-programming !).

Avant de voir quelques méthodes, commençons par initialiser des instances que nous utiliserons par la suite :

java
User bernard = new User("Bernard");
User laverne = new User("Laverne");
User hoagie = new User("Hoagie");
User surfer = new User("Jeff Woodie");
Computer sx386 = new Computer("386SX-25");
Computer dx486 = new Computer("486DX2-66");
Computer c64 = new Computer("Commodore 64");

1. Insertion

L'insertion d'un couple clé-valeur se fait grâce à la méthode put(K key, V value) :

java
Map<User, Computer> classroom = new HashMap<>();
classroom.put(bernard, dx486);
classroom.put(laverne, sx386);
classroom.put(hoagie, sx386);
classroom.put(surfer, c64);

Comme tu peux le remarquer, il est possible d'assigner la même valeur à des clés différentes.

2. Suppression

Tu peux supprimer un élément grâce à sa clé. L'élément supprimé sera renvoyé :

java
Computer removed = classroom.remove(surfer);
System.out.println(removed.getModel()); // Commodore 64

3. Ensemble des clés

Il est possible de récupérer l'ensemble des clés (Set) du tableau associatif avec la méthode keySet() :

java
for (User user : classroom.keySet()) {  
    System.out.println(user.getName());  
}

bash
$ Bernard
$ Hoagie
$ Laverne

Comme les clés sont dans un ensemble, l'ordre n'est pas garanti

4. Collection des valeurs

Il est possible de récupérer la collection des valeurs (Collection) du tableau associatif avec la méthode values() :

java
for (Computer computer : classroom.values()) {  
    System.out.println(computer.getModel());  
}

bash
$ 486DX2-66
$ 386SX-25
$ 386SX-25

Les valeurs sont classées par ordre d'insertion

5. Parcourir l'ensemble des couples clé-valeur

Il est possible de récupérer l'ensemble des couples clé-valeur (Set) du tableau associatif avec la méthode entrySet(). Le type du couple clé-valeur sera Map.Entry<K, V>, de clé K et de valeur V.

Il est possible de parcourir cet ensemble et, pour chaque couple, d'en récupérer la clé avec la méthode getKey(), ainsi que la valeur avec la méthode getValue() :

java
for (Map.Entry<User, Computer> entry : classroom.entrySet()) {  
    User user = entry.getKey();  
    Computer computer = entry.getValue();  
    System.out.println(user.getName() + ": " + computer.getModel());  
}

bash
$ Bernard: 486DX2-66
$ Hoagie: 386SX-25
$ Laverne: 386SX-25

Comme les couples clé-valeur sont dans un ensemble, l'ordre n'est pas garanti

6. Rechercher une clé et une valeur

Il est possible de chercher si un tableau associatif contient une clé ou une valeur, grâce aux méthodes containsKey(Object o) et containsValue(Object o) :

java
System.out.println(classroom.containsKey(bernard)); // true 
System.out.println(classroom.containsValue(c64)); // false

↗
Java T Point : Java HashMap class
javatpoint.com
↗
w3schools : Java HashMap
w3schools.com
↗
Oracle : Class HashMap
docs.oracle.com

LinkedHashMap

Si tu désires avoir un tableau associatif classé par ordre d'insertion des éléments, c'est du côté de LinkedHashMap que tu vas devoir regarder !

Ainsi, le parcours des éléments se fera dans l'ordre dans lequel ils ont été ajoutés :

java
Map<User, Computer> linkedClassroom = new LinkedHashMap<>();  
linkedClassroom.put(bernard, dx486);  
linkedClassroom.put(laverne, sx386);  
linkedClassroom.put(hoagie, sx386);
linkedClassroom.put(surfer, c64);

for (User user : linkedClassroom.keySet()) {  
    Computer computer = linkedClassroom.get(user);  
    System.out.println(user.getName() + ": " + computer.getModel());  
}

bash
$ Bernard: 486DX2-66
$ Laverne: 386SX-25
$ Hoagie: 386SX-25
$ Jeff Woodie: Commodore 64

↗
Java T Point : Java LinkedHashMap class
javatpoint.com
↗
Oracle : Class LinkedHashMap
docs.oracle.com

TreeMap

Un tableau associatif TreeMap permet, quant à lui, de trier les éléments par valeur des clés.

java
Map<User, Computer> treeClassroom = new TreeMap<>();

Mais avec le code tel quel, tu auras l'erreur suivante à la compilation :

shell
class User cannot be cast to class java.lang.Comparable

Il faut donc attribuer aux clés un ordre par défaut :

  • soit en implémentant Comparable préalablement

  • soit au moment de la création du tableau associatif, en instanciant une classe anonyme de Comparator

Ci-dessous, tu peux observer comment créer une instance de TreeMap avec un Comparator :

java
Comparator<User> comparator = new Comparator<User>() {  
    @Override  
    public int compare(User orig, User compare) {  
        return orig.getName().compareTo(compare.getName());  
    }  
};  
Map<User, Computer> treeClassroom = new TreeMap<>(comparator);

treeClassroom.put(bernard, dx486);  
treeClassroom.put(laverne, sx386);  
treeClassroom.put(hoagie, sx386);  
treeClassroom.put(surfer, c64);

Ainsi, le parcours des éléments se fera dans l'ordre alphabétique des clés :

java
for (User user : treeClassroom.keySet()) {
    Computer computer = treeClassroom.get(user);
    System.out.println(user.getName() + ": " + computer.getModel());
}

bash
$ Bernard: 486DX2-66
$ Hoagie: 386SX-25
$ Jeff Woodie: Commodore 64
$ Laverne: 386SX-25

↗
Baeldung : A Guide to TreeMap in Java
baeldung.com
↗
Oracle : Class TreeMap
docs.oracle.com

Bien choisir sa collection

Afin de voir en situation toutes les notions abordées par la quête, récupère le dépôt git donné en ressource.

Après toutes ces quêtes sur les collections, tu dois commencer à te demander lesquelles choisir et dans quels cas !

_Credit _Yash-777

Voici un super diagramme (à garder sous la main) qui va t'aider dans ces lourdes décisions !

_Credit _Ramesh Fadatare

↗
Java Guides : Java Collection Framework Best Practices
javaguides.net
↗
LearnJava : Set vs List vs Map
github.com
↗
Dépôt git
github.com

Challenge

Avengers : Pre-Sequel Part.2

Thanos est finalement devenu fan des Avengers et a décidé, après les avoir plus ou moins ressuscités, d'utiliser son Gant de l'infini afin d'organiser une fête.

Afin d'épater la galerie, il veut offrir à chaque invité sa fleur préférée !

Pour démarrer, fais un Fork du dépôt suivant puis clone-le en local.

Pense bien à faire un Fork, sinon tu ne pourras rien pousser !

Thanos possède des éléments Hero et Flower :

  1. Construire un TreeMap nommé party, avec en clé Hero et valeur Flower

  2. Ajouter dans party les couples clé/valeur suivants :

shell
* *Hulk*, *Thor* et *Scarlet Witch* ont une *Rose *
* *Vision* a une *Tulip *
* *Captain America* a une *Lily*
* *Doctor Strange* et *Black Widow* ont une *Violet*

  1. En une seule ligne, afficher si party contient l'instance begonia

  2. Parcourir le tableau associatif par ordre alphabétique de nom de héros, et afficher, pour chacun d'entre eux, sa fleur attribuée.

Résultat attendu lors de l'exécution de Thanos :

shell
$ false
$ Violet
$ Lily
$ Violet
$ Rose
$ Rose
$ Rose
$ Tulip

Critères de validation

  • Le dépôt GitHub contient bien les fichiers Hero.java et Thanos.java
  • La classe Thanos doit être modifiée
  • La classe Hero peut être modifiée (mais ce n'est pas obligatoire)
  • La classe Thanos se compile sans erreur et affiche dans le terminal un résultat identique à celui présenté précédemment