Guide de Guava Multiset

1. Vue d'ensemble

Dans ce didacticiel, nous explorerons l'une des collections Guava - Multiset . Comme un java.util.Set , il permet un stockage et une récupération efficaces des articles sans commande garantie.

Cependant, contrairement à un ensemble , il permet plusieurs occurrences du même élément en suivant le nombre de chaque élément unique qu'il contient.

2. Dépendance de Maven

Tout d'abord, ajoutons la dépendance goyave :

 com.google.guava guava 29.0-jre 

3. Utilisation de Multiset

Prenons une librairie qui a plusieurs exemplaires de différents livres. Nous pourrions vouloir effectuer des opérations telles que l'ajout d'une copie, l'obtention du nombre de copies et la suppression d'une copie lorsqu'elle est vendue. Comme un ensemble ne permet pas de multiples occurrences du même élément, il ne peut pas gérer cette exigence.

Commençons par ajouter des copies d'un titre de livre. Le Multiset doit renvoyer que le titre existe et nous fournir le décompte correct :

Multiset bookStore = HashMultiset.create(); bookStore.add("Potter"); bookStore.add("Potter"); bookStore.add("Potter"); assertThat(bookStore.contains("Potter")).isTrue(); assertThat(bookStore.count("Potter")).isEqualTo(3);

Supprimons maintenant une copie. Nous nous attendons à ce que le décompte soit mis à jour en conséquence:

bookStore.remove("Potter"); assertThat(bookStore.contains("Potter")).isTrue(); assertThat(bookStore.count("Potter")).isEqualTo(2);

Et en fait, nous pouvons simplement définir le nombre au lieu d'effectuer diverses opérations d'ajout:

bookStore.setCount("Potter", 50); assertThat(bookStore.count("Potter")).isEqualTo(50);

Multiset valide la valeur de comptage . Si nous le définissons sur négatif, une IllegalArgumentException est levée:

assertThatThrownBy(() -> bookStore.setCount("Potter", -1)) .isInstanceOf(IllegalArgumentException.class);

4. Comparaison avec la carte

Sans accès à Multiset , nous pourrions réaliser toutes les opérations ci-dessus en implémentant notre propre logique à l'aide de java.util.Map:

Map bookStore = new HashMap(); // adding 3 copies bookStore.put("Potter", 3); assertThat(bookStore.containsKey("Potter")).isTrue(); assertThat(bookStore.get("Potter")).isEqualTo(3); // removing 1 copy bookStore.put("Potter", 2); assertThat(bookStore.get("Potter")).isEqualTo(2);

Lorsque nous voulons ajouter ou supprimer une copie à l'aide d'une carte , nous devons nous souvenir du décompte actuel et l'ajuster en conséquence. Nous devons également implémenter cette logique dans notre code d'appel à chaque fois ou construire notre propre bibliothèque à cet effet. Notre code devrait également contrôler l' argument de valeur . Si nous ne faisons pas attention, nous pourrions facilement définir la valeur sur null ou négative même si les deux valeurs ne sont pas valides:

bookStore.put("Potter", null); assertThat(bookStore.containsKey("Potter")).isTrue(); bookStore.put("Potter", -1); assertThat(bookStore.containsKey("Potter")).isTrue(); 

Comme nous pouvons le voir, il est beaucoup plus pratique d'utiliser Multiset au lieu de Map .

5. Concurrence

Lorsque nous voulons utiliser Multiset dans un environnement concurrent, nous pouvons utiliser ConcurrentHashMultiset , qui est une implémentation Multiset thread-safe .

Nous devons noter qu'être thread-safe ne garantit pas la cohérence, cependant. L'utilisation des méthodes d' ajout ou de suppression fonctionnera bien dans un environnement multi-thread, mais que se passe-t-il si plusieurs threads appelaient la méthode setCount ?

Si nous utilisons la méthode setCount , le résultat final dépendra de l'ordre d'exécution entre les threads , qui ne peut pas nécessairement être prédit. Les méthodes d' ajout et de suppression sont incrémentielles et ConcurrentHashMultiset est en mesure de protéger leur comportement. La définition directe du nombre n'est pas incrémentielle et peut donc entraîner des résultats inattendus lorsqu'elle est utilisée simultanément.

Cependant, il existe une autre variante de la méthode setCount qui met à jour le décompte uniquement si sa valeur actuelle correspond à l'argument passé. La méthode renvoie true si l'opération a réussi, une forme de verrouillage optimiste:

Multiset bookStore = HashMultiset.create(); // updates the count to 2 if current count is 0 assertThat(bookStore.setCount("Potter", 0, 2)).isTrue(); // updates the count to 5 if the current value is 50 assertThat(bookStore.setCount("Potter", 50, 5)).isFalse();

Si nous voulons utiliser la méthode setCount dans le code simultané, nous devons utiliser la version ci-dessus pour garantir la cohérence. Un client multi-thread peut effectuer une nouvelle tentative si la modification du nombre échoue.

6. Conclusion

Dans ce court didacticiel, nous avons expliqué quand et comment utiliser un multiset, nous l'avons comparé à une carte standard et avons examiné la meilleure façon de l'utiliser dans une application simultanée.

Comme toujours, le code source des exemples se trouve à l'adresse over sur GitHub.