Utilisation de MapMaker de Guava

1. Introduction

MapMaker est une classe de générateur de Guava qui facilite la création de cartes thread-safe.

Java prend déjà en charge WeakHashMap pour utiliser des références faibles pour les clés. Mais, il n'y a pas de solution prête à l'emploi pour utiliser la même chose pour les valeurs. Heureusement, MapMaker fournit des méthodes de générateur simples pour utiliser WeakReference pour les clés et les valeurs .

Dans ce didacticiel, voyons comment MapMaker facilite la création de plusieurs cartes et l'utilisation de références faibles.

2. Dépendance de Maven

Tout d'abord, ajoutons la dépendance Google Guava, disponible sur Maven Central:

 com.google.guava guava 29.0-jre 

3. Un exemple de mise en cache

Considérons un scénario simple d'un serveur conservant quelques caches pour les utilisateurs: un cache de session et un cache de profil.

Le cache de session est de courte durée et ses entrées deviennent invalides une fois que l'utilisateur n'est plus actif. Ainsi, le cache peut supprimer l'entrée de l'utilisateur une fois que l'objet utilisateur a été récupéré.

Cependant, le cache de profil peut avoir une durée de vie plus longue (TTL). Les entrées du cache de profil ne deviennent invalides que lorsque l'utilisateur met à jour son profil.

Dans ce cas, le cache ne peut supprimer l'entrée que lorsque l'objet de profil est récupéré.

3.1. Structures de données

Créons des classes pour représenter ces entités.

Nous allons commencer par l'utilisateur:

public class User { private long id; private String name; public User(long id, String name) { this.id = id; this.name = name; } public long getId() { return id; } public String getName() { return name; } }

Puis la séance:

public class Session { private long id; public Session(long id) { this.id = id; } public long getId() { return id; } } 

Et enfin le profil:

public class Profile { private long id; private String type; public Profile(long id, String type) { this.id = id; this.type = type; } public long getId() { return id; } public String getName() { return type; } }

3.2. Créer les caches

Créons une instance de ConcurrentMap pour le cache de session à l'aide de la méthode makeMap :

ConcurrentMap sessionCache = new MapMaker().makeMap();

La mappe retournée n'autorise pas les valeurs nulles pour la clé et la valeur.

Maintenant, créons une autre instance de ConcurrentMap pour le cache de profil:

ConcurrentMap profileCache = new MapMaker().makeMap();

Notez que nous n'avons pas spécifié la capacité initiale des caches. Ainsi, MapMaker crée une carte de capacité 16 par défaut.

Si nous le voulons, nous pouvons modifier la capacité en utilisant la méthode initialCapacity :

ConcurrentMap profileCache = new MapMaker().initialCapacity(100).makeMap();

3.3. Modification du niveau de concurrence

MapMaker définit la valeur par défaut du niveau de concurrence sur 4 . Cependant, sessionCache doit prendre en charge un nombre plus élevé de mises à jour simultanées sans aucune contention de thread.

Ici, la méthode du générateur concurrencyLevel vient à la rescousse:

ConcurrentMap sessionCache = new MapMaker().concurrencyLevel(10).makeMap();

3.4. Utilisation de références faibles

Les cartes que nous avons créées ci-dessus utilisent des références solides pour les clés et les valeurs. Ainsi, les entrées restent dans la carte même si les clés et les valeurs sont récupérées. Nous devrions plutôt utiliser des références faibles.

Une entrée sessionCache n'est pas valide après la récupération de la clé (l'objet utilisateur). Alors, utilisons des références faibles pour les clés:

ConcurrentMap sessionCache = new MapMaker().weakKeys().makeMap();

Pour le profileCache , nous pouvons utiliser des références faibles pour les valeurs:

ConcurrentMap profileCache = new MapMaker().weakValues().makeMap();

Lorsque ces références sont récupérées, Guava garantit que ces entrées ne seront incluses dans aucune des opérations de lecture ou d'écriture suivantes sur la carte . Cependant, la méthode size () peut parfois être incohérente et peut inclure ces entrées .

4. Composants internes de MapMaker

MapMaker crée un ConcurrentHashMap par défaut si les références faibles ne sont pas activées . Les vérifications d'égalité s'effectuent via la méthode habituelle d'égalité.

Si nous activons les références faibles, MapMaker crée une carte personnalisée représentée par un ensemble de tables de hachage en interne. Il partage également des caractéristiques de performances similaires à celles d'un ConcurrentHashMap .

Cependant, une différence importante avec WeakHashMap est que les vérifications d'égalité s'effectuent via les comparaisons d' identité (== et identityHashCode ).

5. Conclusion

Dans ce court article, nous avons appris à utiliser la classe MapMaker pour créer une carte thread-safe. Nous avons également vu comment personnaliser la carte pour utiliser des références faibles.

Comme toujours, le code source complet de l'article est disponible à l'adresse over sur GitHub.