CodeGym /Cours /JAVA 25 SELF /EnumSet/EnumMap

EnumSet/EnumMap

JAVA 25 SELF
Niveau 28, Leçon 3
Disponible

1. Introduction

En Java, il existe des collections spécifiques pour travailler avec les énumérations (enum) : EnumSet et EnumMap. Elles font partie de la bibliothèque standard (java.util) et sont conçues pour une efficacité maximale avec les types enum.

Comment sont conçus EnumSet et EnumMap en interne ?

EnumSet fonctionne comme un masque binaire. Imaginez un ensemble d’interrupteurs où chaque élément d’énumération occupe exactement un bit. Si le bit est activé — l’élément est présent dans l’ensemble ; s’il est désactivé — il n’y est pas. Tout est stocké dans un tableau de nombres (long[]), et si votre enum comporte moins de 64 valeurs, l’ensemble entier tient dans un seul nombre !

EnumMap est encore plus simple : c’est un tableau de valeurs dont l’indice est le numéro ordinal (ordinal) de l’élément enum. Au lieu d’un HashMap<Enum, V> habituel, vous obtenez une structure très compacte et rapide.

Qu’est-ce que cela apporte ? Les opérations d’ajout, de suppression et de vérification s’exécutent en O(1). La mémoire utilisée est minimale (surtout comparée à HashSet et HashMap). L’itération des éléments suit toujours l’ordre de déclaration dans l’enum, ce qui rend le résultat prédictible.

Exemple : à quoi cela ressemble

enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }

EnumSet<Day> weekend = EnumSet.of(Day.SAT, Day.SUN);
System.out.println(weekend); // [SAT, SUN]

De l’extérieur, c’est un ensemble classique. Mais en interne — un nombre avec deux bits positionnés : l’un pour SAT, l’autre pour SUN. Ajoutez FRI — un bit supplémentaire sera activé. Pas de table de hachage ni d’objets superflus.

EnumMap<Day, String> schedule = new EnumMap<>(Day.class);
schedule.put(Day.MON, "Gym");
schedule.put(Day.FRI, "Party");
System.out.println(schedule); // {MON=Gym, FRI=Party}

Ici, les clés (Day) sont converties en indices internes de tableau ; l’accès est donc aussi rapide qu’un accès à un élément de tableau.

2. Cas d’utilisation : drapeaux, tableaux, automates finis

EnumSet : idéal pour les drapeaux et ensembles d’états

  • Drapeaux : stocker des ensembles « activé/désactivé » pour un nombre limité d’options.
  • Ensembles de valeurs enum : jours de la semaine, permissions utilisateur, états d’une tâche.
  • Automates finis (FSM) : pratique pour stocker les transitions autorisées dans un EnumSet.

Exemple : indicateurs d’accès

enum Permission { READ, WRITE, EXECUTE }

EnumSet<Permission> perms = EnumSet.of(Permission.READ, Permission.WRITE);
if (perms.contains(Permission.WRITE)) {
    // Écriture autorisée
}

Exemple : toutes les valeurs sauf certaines

EnumSet<Day> workdays = EnumSet.complementOf(EnumSet.of(Day.SAT, Day.SUN));
System.out.println(workdays); // [MON, TUE, WED, THU, FRI]

EnumMap : idéal pour les tableaux indexés par des clés enum

  • Tables de correspondance : les clés sont des valeurs enum, les valeurs — n’importe quels objets.
  • Accès rapide : plus rapide et plus compact qu’un HashMap<Enum, V>.

Exemple : prix par jour de la semaine

EnumMap<Day, Integer> prices = new EnumMap<>(Day.class);
prices.put(Day.MON, 100);
prices.put(Day.SAT, 200);
System.out.println(prices.get(Day.SAT)); // 200

Exemple : automate fini

enum State { START, RUNNING, STOPPED }
EnumMap<State, EnumSet<State>> transitions = new EnumMap<>(State.class);
transitions.put(State.START, EnumSet.of(State.RUNNING));
transitions.put(State.RUNNING, EnumSet.of(State.STOPPED));
transitions.put(State.STOPPED, EnumSet.noneOf(State.class));

3. Pièges : modification de l’enum et sérialisation

EnumSet et EnumMap dépendent de l’ensemble et de l’ordre des valeurs de votre enum. Si vous ajoutez un nouvel élément, supprimez un ancien ou en changez l’ordre, les collections sauvegardées ou sérialisées peuvent se comporter de manière incorrecte.

Sérialisation

  • EnumSet et EnumMap sont sérialisables, mais si l’enum change entre sérialisation et désérialisation, des erreurs/pertes de données sont possibles.
  • La suppression d’une valeur de l’enum après sérialisation conduit presque à coup sûr à une exception lors de la lecture.

Bonne pratique :

  • Ne sérialisez pas EnumSet/EnumMap si vous n’êtes pas certain que l’enum est stable.
  • Pour un stockage à long terme, utilisez par exemple une liste de représentations sous forme de chaîne des valeurs.

4. Détails utiles

Comparaison d’EnumSet/EnumMap et des collections classiques

Collection Clé/élément Implémentation interne Performances Mémoire Null
EnumSet
enum
masque binaire O(1) très peu interdit
HashSet<Enum>
enum
table de hachage O(1) plus autorisé
EnumMap
enum
tableau indexé par ordinal O(1) très peu interdit
HashMap<Enum, V>
enum
table de hachage O(1) plus autorisé

Bonnes pratiques

  • Utilisez EnumSet pour les ensembles de valeurs (of, noneOf, allOf, complementOf).
  • Utilisez EnumMap pour les tables d’association où la clé est un enum.
  • Évitez les énumérations « géantes » — des listes de centaines de valeurs nuisent à la compacité.
  • Ne sérialisez pas si l’enum peut changer.
  • N’utilisez pas null comme clé ou valeur.

5. Pratique : comment utiliser EnumSet et EnumMap dans une application

Exemple : stockage des rôles utilisateur

enum Role { USER, ADMIN, MODERATOR }

class User {
    private EnumSet<Role> roles = EnumSet.noneOf(Role.class);

    public void addRole(Role role) {
        roles.add(role);
    }

    public boolean isAdmin() {
        return roles.contains(Role.ADMIN);
    }
}

Exemple : table de transitions d’états

enum State { NEW, IN_PROGRESS, DONE }

EnumMap<State, EnumSet<State>> transitions = new EnumMap<>(State.class);
transitions.put(State.NEW, EnumSet.of(State.IN_PROGRESS));
transitions.put(State.IN_PROGRESS, EnumSet.of(State.DONE));
transitions.put(State.DONE, EnumSet.noneOf(State.class));

6. Erreurs courantes avec EnumSet/EnumMap

Erreur n° 1 : Utiliser EnumSet/EnumMap avec un type non enum.
Ces collections ne fonctionnent qu’avec des types qui sont des enum.

// EnumSet<String> set = EnumSet.of("A", "B"); // Erreur de compilation !

Erreur n° 2 : Utiliser EnumSet/EnumMap pour des enum très volumineux.
Des énumérations de plusieurs centaines de valeurs réduisent la compacité. Néanmoins, cela reste souvent meilleur en mémoire qu’un HashSet/HashMap pour les mêmes données.

Erreur n° 3 : Modifier l’enum après sérialisation.
Ajouter/supprimer/réordonner des valeurs après la sérialisation entraîne des erreurs à la lecture ou une perte de données.

Erreur n° 4 : Utiliser EnumSet/EnumMap avec null.
Ni les éléments de EnumSet, ni les clés/valeurs de EnumMap ne peuvent être null.

EnumSet<Day> days = EnumSet.of(null); // NullPointerException!
EnumMap<Day, String> map = new EnumMap<>(Day.class);
map.put(null, "test"); // NullPointerException!

Erreur n° 5 : Penser qu’EnumSet est un Set « ordinaire ».
EnumSet ne stocke que les valeurs de son enum ; vous ne pouvez pas y ajouter un objet extérieur à l’énumération.

Erreur n° 6 : Utiliser EnumSet/EnumMap avec des enum « modifiables ».
Si des énumérations sont générées ou substituées à l’exécution (chargement dynamique/réflexion), ces structures ne fonctionneront pas correctement.

1
Mission
JAVA 25 SELF, niveau 28, leçon 3
Bloqué
Planification du week-end idéal 🏖️
Planification du week-end idéal 🏖️
1
Mission
JAVA 25 SELF, niveau 28, leçon 3
Bloqué
Tarification dynamique à la galerie d'art "Inspiration" 🎨
Tarification dynamique à la galerie d'art "Inspiration" 🎨
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION