Tutoriel pour découvrir l’API Collections de Google Guava

La classe Sets

Rony Rauzduel

L'objectif de ce quatrième article sur l’exploration de la bibliothèque Guava de Google est de vous présenter les méthodes statiques composant la classe utilitaire Sets.

Cet article nous permettra d’apprécier toute la richesse de cette bibliothèque qui ne fait que s’améliorer au fur et à mesure des versions.

Lors de l’écriture de cet article, nous avons utilisé la version 30.1.1 de Google Guava.

Ce tutoriel est avant tout pratique, mon fil conducteur est la page suivante https://github.com/google/guava/wiki/ImmutableCollectionsExplainedained traitant des Collections de l'API Google Guava.

Vous pouvez également consulter mes précédents tutoriels pour découvrir la bibliothèque Guava.

La classe Lists

La classe ImmutableList

La classe Iterables

Pour réagir au contenu de cet article, un espace de dialogue vous est proposé sur le forum. Commentez

- Titre : Tutoriel pour découvrir l’API Collections de Google Guava

- Auteur : Rony Rauzduel

- Parution : 12 novembre 2021

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2021 Rony Rauzduel. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Ajouter Google Guava à votre projet (Rappel)

- III. La classe utilitaire Sets

- IV. Implémentation des méthodes de la classe Sets

- IV-A. Les imports

- IV-B. Les attributs, les blocs et les méthodes statiques

- IV-C. La méthode cartesianProduct

- IV-C-1. cartesianProduct(Set<? extends B>... sets)

- IV-C-2. cartesianProduct(List<? extends Set<? extends B>> sets)

- IV-D. La méthode combinations

- IV-E. La méthode complementOf

- IV-F. La méthode difference

- IV-G. La méthode filter

- IV-G-1. filter(NavigableSet<E> unfiltered, Predicate<? super E> predicate)

- IV-G-2. filter(Set<E> unfiltered, Predicate<? super E> predicate)

- IV-G-3. filter(SortedSet<E> unfiltered, Predicate<? super E> predicate)

- IV-H. La méthode immutableEnumSet

- IV-H-1. immutableEnumSet(E anElement, E... otherElements)

- IV-H-2. immutableEnumSet(Iterable<E> elements)

- IV-I. La méthode intersection

- IV-J. La méthode newConcurrentHashSet

- IV-J-1. newConcurrentHashSet()

- IV-J-2. newConcurrentHashSet(Iterable<? extends E> elements)

- IV-K. La méthode newCopyOnWriteArraySet

- IV-K-1. newCopyOnWriteArraySet()

- IV-K-2. newCopyOnWriteArraySet(Iterable<? extends E> elements)

- IV-L. La méthode newEnumSet

- IV-M. La méthode newHashSet

- IV-M-1. newHashSet()

- IV-M-2. newHashSet(E... elements)

- IV-M-3. newHashSet(Iterable<? extends E> elements)

- IV-M-4. newHashSet(Iterator<? extends E> elements)

- IV-N. La méthode newHashSetWithExpectedSize

- IV-O. La méthode newIdentityHashSet

- IV-P. La méthode newLinkedHashSet

- IV-P-1. newLinkedHashSet()

- IV-P-2. newLinkedHashSet(Iterable<? extends E> elements)

- IV-Q. La méthode newLinkedHashSetWithExpectedSize

- IV-R. La méthode newTreeSet

- IV-R-1. newTreeSet()

- IV-R-2. newTreeSet(Comparator<? super E> comparator)

- IV-R-3. newTreeSet(Iterable<? extends E> elements)

- IV-S. La méthode powerSet

- IV-T. La méthode subSet

- IV-U. La méthode symmetricDifference

- IV-V. La méthode synchronizedNavigableSet

- IV-W. La méthode toImmutableEnumSet

- IV-X. La méthode union

- IV-Y. La méthode unmodifiableNavigableSet

- V. Conclusion

- VI. Remerciements

I - Introduction

Sans conteste, la meilleure manière de découvrir une bibliothèque est de parcourir chacune de ses méthodes et de les implémenter les unes après les autres pour comprendre les subtilités de chacune.

Ce tutoriel sur l’exploration de la bibliothèque Guava de Google reprend l’ensemble des méthodes statiques composant la classe utilitaire Sets.

II - Ajouter Google Guava à votre projet (Rappel)

La bibliothèque Google Guava peut être ajoutée de plusieurs façons dans un projet. La façon la plus triviale est de récupérer le .jar depuis le site de Google, puis de placer ce dernier directement dans la bibliothèque de votre projet.

Une autre façon plus élégante est d’utiliser un outil industrialisé de build, comme Apache Maven ou Gradle, en y ajoutant la dépendance requise de Google Guava.

Installer Guava avec Maven

1. <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->

2. <dependency>

3. <groupId>com.google.guava</groupId>

4. <artifactId>guava</artifactId>

5. <version>30.1-jre</version>

6. </dependency>

III - La classe utilitaire Sets

Force est de constater que tout développeur familier avec le langage Java connaît l’utilité de pouvoir parcourir des implémentations (notamment List et Set) de manière uniforme sans devoir se soucier des mécanismes internes.

Google Guava est allé encore plus loin dans l’implémentation des fonctionnalités sous-jacentes à l’utilisation des objets itérables. Les développeurs de Google ont conçu de nombreuses méthodes utilitaires statiques permettant une gestion efficiente lorsque l’on travaille avec des objets sur lesquels on peut itérer.

Mieux qu’un long discours, dans le chapitre suivant, nous mettrons en œuvre la richesse de cette bibliothèque à travers un cas pratique de gestion d’un parc automobile.

IV - Implémentation des méthodes de la classe Sets

IV-A - Les imports

Les imports

1. import com.google.common.collect.ImmutableList;

2. import com.google.common.collect.ImmutableSet;

3. import com.google.common.collect.Range;

4. import com.google.common.collect.Sets;

5. import static java.lang.System.out;

6. import java.util.Arrays;

7. import java.util.Comparator;

8. import java.util.EnumSet;

9. import java.util.HashSet;

10. import java.util.Iterator;

11. import java.util.List;

12. import java.util.NavigableSet;

13. import java.util.Set;

14. import java.util.SortedSet;

15. import java.util.TreeSet;

16. import java.util.concurrent.CopyOnWriteArraySet;

17. import java.util.function.Predicate;

18. import java.util.function.Supplier;

19. import java.util.stream.Collector;

20. import java.util.stream.Collectors;

IV-B - Les attributs, les blocs et les méthodes statiques

Créons une classe Application qui contiendra les attributs, les blocs et les méthodes statiques pour la gestion de notre parc automobile.

Les attributs et blocs statiques

1. public final class Application {

3. // les constantes

4. private final static int NB_MARQUES_VEHICULES = 5;

5. private final static int NB_COULEURS_VEHICULES = 5;

6. private final static int NB_VEHICULES = 3;

8. // les enums

9. private final static Enum categorie, marque, modele, annee, cylindree, puissance, soupapes, injection;

11. private static enum Caracteristique {

12. CATEGORIE,

13. MARQUE,

14. MODELE,

15. ANNEE,

16. CYLINDREE,

17. PUISSANCE,

18. SOUPAPES,

19. INJECTION

20. }

22. static {

23. // caractéristiques constructeur

24. categorie = Caracteristique.CATEGORIE;

25. marque = Caracteristique.MARQUE;

26. modele = Caracteristique.MODELE;

27. annee = Caracteristique.ANNEE;

28. // caractéristiques motrice

29. cylindree = Caracteristique.CYLINDREE;

30. puissance = Caracteristique.PUISSANCE;

31. soupapes = Caracteristique.SOUPAPES;

32. injection = Caracteristique.INJECTION;

33. }

35. private static enum Marque {

36. CITROEN,

37. OPEL,

38. PEUGEOT,

39. RENAULT,

40. TOYOTA,

41. VOLKSWAGEN,

42. MITSUBISHI

43. }

45. private final static String[] peugeot;

46. private final static String[] opel;

47. private final static String[] toyota;

48. private final static String[] renault;

49. private final static String[] citroen;

50. private final static HashSet<String> vehicules;

52. static { // les structures de données

53. peugeot = new String[NB_VEHICULES];

54. peugeot[0] = "Peugeot Nouvelle 208";

55. peugeot[1] = "Peugeot Traveller/Expert Combi";

56. peugeot[2] = "Peugeot Nouvelle SUV 5008";

58. toyota = new String[NB_VEHICULES];

59. toyota[0] = "Toyota Nouvelle Yaris";

60. toyota[1] = "Toyota Avensis";

61. toyota[2] = "Toyota Nouvelle Landcruiser";

63. renault = new String[NB_VEHICULES];

64. renault[0] = "Renault Clio";

65. renault[1] = "Renault Nouveau Grand Scenic";

66. renault[2] = "Renault Arkana";

68. citroen = new String[NB_VEHICULES];

69. citroen[0] = "Citroën Nouvelle C3";

70. citroen[1] = "Citroën Berlingo";

71. citroen[2] = "Citroën Space Tourer Business Lounge";

73. opel = new String[NB_VEHICULES];

74. opel[0] = "Opel Corsa";

75. opel[1] = "Opel Zafira";

76. opel[2] = "Opel Crossland X";

78. vehicules = new HashSet<>(NB_MARQUES_VEHICULES);

79. vehicules.addAll(Arrays.asList(peugeot));

80. }

82. // les predicats

83. private final static Predicate<String> marqueVolkswagen;

84. private final static Predicate<String> contientCouleurGris;

85. private final static Predicate<String> marqueMitsubishi;

87. static {

88. contientCouleurGris = c -> c.contains("Gris");

89. marqueVolkswagen = v -> v.contains("Volkswagen");

90. marqueMitsubishi = v -> v.contains("Mitsubishi");

91. }

93. private final static HashSet<String> couleurs;

95. static { // les différentes peintures

96. couleurs = new HashSet<>(NB_COULEURS_VEHICULES);

97. couleurs.add("Gris Graphite");

98. couleurs.add("Gris Futura");

99. couleurs.add("Rouge Aden");

100. couleurs.add("Gris Thorium");

101. couleurs.add("Noir Obsidien");

102. }

104. private final static HashSet<String> peintures;

106. static { // les différents coloris

107. peintures = new HashSet<>();

108. peintures.add("Noir Obsidien");

109. peintures.add("Gris Thorium");

110. peintures.add("Gris Cassiopée");

111. peintures.add("Gris Cassiopée"); // un élément dupliqué ?!

112. peintures.add("Gris Manitoba");

113. peintures.add("Gris Shark");

114. peintures.add("Bourrasque");

115. peintures.add("Bourrasque"); // un autre élément dupliqué ?!

116. peintures.add("Bleu Encre");

117. }

119. private final static HashSet<String> carburants;

121. static { // les types de carburants

122. carburants = new HashSet<>();

123. carburants.add("Essence");

124. carburants.add("Diesel");

125. carburants.add("GNV"); // Gaz naturel pour véhicules

126. carburants.add("Hybride");

127. carburants.add("Biodiesel");

128. carburants.add("Electric");

129. }

131. private final static NavigableSet<String> mitsubishi;

133. static {

134. mitsubishi = new TreeSet<>();

135. mitsubishi.add("Mitsubishi Space Star");

136. mitsubishi.add("Mitsubishi Outlander");

137. mitsubishi.add("Mitsubishi Outlander PHEV");

138. mitsubishi.add("Mitsubishi L200");

139. mitsubishi.add("Mitsubishi Eclipse Cross PHEV");

140. mitsubishi.add("Mitsubishi SUV ASX");

141. }

143. private final static SortedSet<String> volkswagen;

145. static {

146. volkswagen = new TreeSet<>();

147. volkswagen.add("Volkswagen Polo");

148. volkswagen.add("Volkswagen Golf");

149. volkswagen.add("Volkswagen Passat");

150. volkswagen.add("Volkswagen Tiguan");

151. volkswagen.add("Volkswagen Caddy");

152. volkswagen.add("Volkswagen Arteon");

153. volkswagen.add("Volkswagen Amarok");

154. volkswagen.add("Volkswagen Vitto");

155. volkswagen.add("Volkswagen T-cross");

156. volkswagen.add("Volkswagen Sharan");

157. }

159. private final static Set<String> audi;

161. static {

162. audi = new HashSet<>();

164. audi.add("Audi A1 Sportback");

165. audi.add("Audi Nouvelle A3 Sportback");

166. audi.add("Audi A4 Berline");

167. audi.add("Audi A5 Sportback");

168. audi.add("Audi A6 Berline TFSI");

169. audi.add("Audi A7 Sportback TFSI e");

170. audi.add("Audi Nouvelle A8 L TFSI e");

171. }

173. private final static Set<Integer> cylindrees; // en cm3

175. static {

176. cylindrees = Sets.newHashSet();

177. cylindrees.add(999);

178. cylindrees.add(1300);

179. cylindrees.add(1461);

180. cylindrees.add(1461); // un élément dupliqué ?!

181. cylindrees.add(1560);

182. cylindrees.add(1700);

183. cylindrees.add(1983);

184. cylindrees.add(2398);

185. cylindrees.add(3400);

186. cylindrees.add(3400); // un autre élément dupliqué ?!

187. cylindrees.add(5500);

188. cylindrees.add(5935);

189. }

191. private final static NavigableSet<Integer> puissances; // en kiloWatt

193. static {

194. puissances = new TreeSet<>();

195. puissances.add(178);

196. puissances.add(224);

197. puissances.add(285);

198. puissances.add(314);

199. puissances.add(347);

200. puissances.add(525);

201. puissances.add(624);

202. puissances.add(705);

203. puissances.add(826);

204. puissances.add(999);

205. puissances.add(1300);

206. }

208. public static void main(String[] args) {

209. // commençons par afficher la liste des véhicules de la marque Peugeot

210. out.println(Arrays.asList(peugeot)); // [Peugeot Nouvelle 208, Peugeot Traveller/Expert Combi, Peugeot Nouvelle SUV 5008]

211. }

212. }

IV-C - La méthode cartesianProduct

Cette méthode permet de renvoyer toutes les listes possibles qui peuvent être formées en choisissant un élément de chacun des ensembles donnés dans l'ordre ; le "produit cartésien n-aire" des ensembles.

IV-C-1 - cartesianProduct(Set<? extends B>... sets)

Nous voulons renvoyer toutes les listes possibles qui peuvent être formées en choisissant un élément de chacun des ensembles donnés dans l'ordre. En quelque sorte nous voulons afficher un produit cartésien d’un ensemble ordonné d’éléments. La méthode cartesianProduct(Set<? extends B>... sets) permet d’effectuer cette action.

Méthode cartesianProduct(Set<? extends B>... sets)

1. final Set<List<String>> immutableCartesianProduct = Sets.cartesianProduct(vehicules, peintures);

2. // 1ère méthode : on utilise directement la méthode cartesianProduct(List<? extends Set<? extends B>> sets)

3. out.println(immutableCartesianProduct);

4. // 2ème méthode : on utilise une boucle pour parcourir les couples formés par le produit cartésien souhaité [véhicule, peinture]

5. final List<String> peugeotMutableList = Arrays.asList(peugeot);

6. peugeotMutableList.forEach(vehicule -> {

7. peintures.stream()

8. //.filter(carburant -> carburant.equals("Diesel")) // si on souhaite uniquement les véhicules "Diesel"

9. .map(peinture -> ImmutableList.of(vehicule, peinture))

10. .forEachOrdered(couple -> {

11. out.println(couple);

12. });

13. });

14. /*

15. [[Peugeot Traveller/Expert Combi, Gris Manitoba], [Peugeot Traveller/Expert Combi, Gris Shark], [Peugeot Traveller/Expert Combi, Bourrasque], [Peugeot Traveller/Expert Combi, Noir Obsidien], [Peugeot Traveller/Expert Combi, Gris Thorium], [Peugeot Traveller/Expert Combi, Gris Cassiopée], [Peugeot Traveller/Expert Combi, Bleu Encre], [Peugeot Nouvelle SUV 5008, Gris Manitoba], [Peugeot Nouvelle SUV 5008, Gris Shark], [Peugeot Nouvelle SUV 5008, Bourrasque], [Peugeot Nouvelle SUV 5008, Noir Obsidien], [Peugeot Nouvelle SUV 5008, Gris Thorium], [Peugeot Nouvelle SUV 5008, Gris Cassiopée], [Peugeot Nouvelle SUV 5008, Bleu Encre], [Peugeot Nouvelle 208, Gris Manitoba], [Peugeot Nouvelle 208, Gris Shark], [Peugeot Nouvelle 208, Bourrasque], [Peugeot Nouvelle 208, Noir Obsidien], [Peugeot Nouvelle 208, Gris Thorium], [Peugeot Nouvelle 208, Gris Cassiopée], [Peugeot Nouvelle 208, Bleu Encre]]

16. */

IV-C-2 - cartesianProduct(List<? extends Set<? extends B>> sets)

Une autre signature de cette méthode permet d’utiliser directement une liste immuable de sets eux-mêmes immuables.

Méthode cartesianProduct(List<? extends Set<? extends B>> sets)

1. final Set<List<String>> immutableCartesianProducts = Sets.cartesianProduct(

2. ImmutableList.of(

3. ImmutableSet.of(citroen[0], citroen[1]),

4. ImmutableSet.of("Gris Graphite", "Gris Shark", "Gris Manitoba"))

5. );

6. out.println(immutableCartesianProducts);

7. /*

8. [[Citroën Nouvelle C3, Gris Graphite], [Citroën Nouvelle C3, Gris Shark], [Citroën Nouvelle C3, Gris Manitoba], [Renault Arkana, Gris Graphite], [Renault Arkana, Gris Shark], [Renault Arkana, Gris Manitoba]]

9. */

IV-D - La méthode combinations

Nous voulons maintenant récupérer tous les sous-ensembles d’un ensemble d’une longueur que nous avons définie. La méthode combinations(Set<E> set, int size) permet d’effectuer cette opération.

Méthode combinations(Set<E> set, int size)

1. final Set<Set<String>> vehiculeCombinations = Sets.combinations(peintures, 2);

2. out.println(vehiculeCombinations); // Sets.combinations([Gris Manitoba, Gris Shark, Bourrasque, Noir Obsidien, Gris Thorium, Gris Cassiopée, Bleu Encre], 2)

3. out.println(vehiculeCombinations.size()); // 21 combinaisons formées

4. /* {{Gris Manitoba, Gris Shark}, {Gris Manitoba, Bourrasque}, {Gris     Manitoba, Noir Obsidien}, {Gris Manitoba, Gris Thorium}, {Gris Manitoba, Gris Cassiopée}, {Gris Manitoba, Bleu Encre}

5. ,{Gris Shark, Bourrasque}, {Gris Shark, Noir Obsidien}, {Gris Shark, Gris Thorium}, {Gris Shark, Gris Cassiopée}, {Gris Shark, Bleu Encre}

6. ,{Bourrasque, Noir Obsidien},{Bourrasque, Gris Thorium}, {Bourrasque, Gris Cassiopée}, {Bourrasque, Bleu Encre}

7. ,{Noir Obsidien, Gris Thorium}, {Noir Obsidien, Gris Cassiopée}, {Noir Obsidien, Bleu Encre}

8. ,{Gris Thorium, Gris Cassiopée},{Gris Thorium, Bleu Encre}

9. ,{Gris Cassiopée, Bleu Encre}}

10. */

IV-E - La méthode complementOf

Nous voulons désormais créer un EnumSet, c’est-à-dire une énumération d’un ensemble ordonné d’éléments récupérant toutes les valeurs qui ne sont pas dans une collection passée en paramètre. La méthode complementOf(Collection<E> collection) permet d’effectuer cette opération sans aucune difficulté.

Méthode complementOf(Collection<E> collection)

1. final EnumSet<Marque> marquesFrancaises, complementOfMarquesFrancaises, complementOfMarquesEtrangeres;

2. marquesFrancaises = EnumSet.of(Marque.CITROEN, Marque.PEUGEOT, Marque.RENAULT);

3. complementOfMarquesFrancaises = EnumSet.complementOf(marquesFrancaises);

4. out.println(complementOfMarquesFrancaises); // [OPEL, TOYOTA, VOLKSWAGEN, MITSUBISHI]

5. complementOfMarquesEtrangeres = EnumSet.complementOf(complementOfMarquesFrancaises);

6. out.println(complementOfMarquesEtrangeres); // [CITROEN, PEUGEOT, RENAULT]

IV-F - La méthode difference

Si nous souhaitons obtenir une vue non modifiable de la différence entre deux sets, la méthode difference(Set<E> set1, Set<?> set2) permet d’accomplir cette tâche.

Méthode difference(Set<E> set1, Set<?> set2)

1. final Sets.SetView<String> colorisDifference = Sets.difference(couleurs, peintures);

2. out.println(colorisDifference); // renvoi tous les éléments présents dans couleurs et qui ne sont pas dans peintures

3. // [Gris Graphite, Gris Futura, Rouge Aden]

IV-G - La méthode filter

Cette méthode permet de renvoyer les éléments non filtrés d'un NavigableSet, qui satisfont à un prédicat.

IV-G-1 - filter(NavigableSet<E> unfiltered, Predicate<? super E> predicate)

Une première signature de cette méthode permet de renvoyer tous les éléments d’un NavigableSet qui répondent à une condition (un prédicat). Par exemple, nous voulons un filtre sur tous les véhicules de la marque Mitsubishi précisément.

Méthode filter(NavigableSet<E> unfiltered, Predicate<? super E> predicate)

1. final NavigableSet<String> filterMitsubishiVehicules = Sets.filter(mitsubishi, marqueMitsubishi::test);

2. out.println(filterMitsubishiVehicules); // [Mitsubishi Eclipse Cross PHEV, Mitsubishi Outlander PHEV, Mitsubishi SUV ASX, Mitsubishi Space Star]

IV-G-2 - filter(Set<E> unfiltered, Predicate<? super E> predicate)

Une autre signature permet cette fois-ci de renvoyer tous les éléments d’un Set qui répondent à un prédicat. Nous voulons toutes les peintures contenant du gris.

Méthode filter(Set<E> unfiltered, Predicate<? super E> predicate)

1. final Set<String> filterCouleursVehicules = Sets.filter(peintures, contientCouleurGris::test);

2. out.println(filterCouleursVehicules); // [Gris Manitoba, Gris Shark, Gris Thorium, Gris Cassiopée]

IV-G-3 - filter(SortedSet<E> unfiltered, Predicate<? super E> predicate)

Une autre signature permet cette fois-ci de renvoyer tous les éléments d’un SortedSet (un ensemble d’éléments triés) qui répondent à un prédicat. Nous voulons récupérer précisément tous les véhicules de la marque Volkswagen.

Méthode filter(SortedSet<E> unfiltered, Predicate<? super E> predica

1. final SortedSet<String> filterVolkswagenVehicules = Sets.filter(volkswagen, marqueVolkswagen::test);

2. out.println(filterVolkswagenVehicules); // [Volkswagen Amarok, Volkswagen Golf, Volkswagen Polo, Volkswagen Sharan, Volkswagen T-cross, Volkswagen Vitto]

IV-H - La méthode immutableEnumSet

Cette méthode permet de renvoyer une instance d'ensembles immuables contenant les éléments de l'énumération donnée.

IV-H-1 - immutableEnumSet(E anElement, E... otherElements)

Une première signature de cette méthode permet de renvoyer une instance immuable d’un set contenant les éléments sélectionnés d’une énumération. Le second paramètre de cette méthode est une ellipse afin que l’on puisse ajouter plusieurs éléments de l’énumération. Nous décidons d’agréger la catégorie, la marque, le modèle et l’année d’un véhicule.

Méthode ImmutableSet<E> immutableEnumSet(E anElement, E... otherElements)

1. final ImmutableSet<String> immutableEnumSetConstructeur = Sets.immutableEnumSet(categorie, marque, modele, annee);

2. out.println(immutableEnumSetConstructeur); // [CATEGORIE, MARQUE, MODELE, ANNEE]

IV-H-2 - immutableEnumSet(Iterable<E> elements)

Une seconde signature de cette méthode prend en unique paramètre un objet Iterable et renvoie tous les éléments de l’énumération qui ont été sélectionnés.

Méthode ImmutableSet<E> immutableEnumSet(Iterable<E> elements)

1. final Iterable<Caracteristique> iterableCaracteristiqueRegalienne = ImmutableList.of(Caracteristique.CATEGORIE, Caracteristique.MARQUE, Caracteristique.MODELE, Caracteristique.ANNEE);

2. final ImmutableSet<Caracteristique> immutableEnumSetCaracteristiqueVehicules = Sets.immutableEnumSet(iterableCaracteristiqueRegalienne);

3. out.println(immutableEnumSetCaracteristiqueVehicules); // [CATEGORIE, MARQUE, MODELE, ANNEE]

IV-I - La méthode intersection

Admettons que nous voulions connaître les éléments communs à deux ensembles. Pour cela nous utilisons la méthode intersection(Set<E> set1, Set<?> set2).

Méthode intersection(Set<E> set1, Set<?> set2)

1. final Sets.SetView<String> intersectionColoris = Sets.intersection(couleurs, peintures);

2. out.println(intersectionColoris); // [Noir Obsidien, Gris Thorium]

IV-J - La méthode newConcurrentHashSet

Cette méthode permet d’instancier un HashSet pouvant fonctionner correctement dans un environnement concurrentiel tout en s’appuyant sur une table de hachage.

IV-J-1 - newConcurrentHashSet()

Nous pouvons également instancier un type HashSet sécurisé (c’est-à-dire pouvant s’utiliser dans un environnement concurrentiel sans altération des données) s’appuyant sur une table de hachage. La méthode newConcurrentHashSet() permet d’effectuer cette action.

Méthode newConcurrentHashSet()

1. final Set<String> newConcurrentHashSetVehicules = Sets.newConcurrentHashSet();

2. newConcurrentHashSetVehicules.add("Citroën Space Tourer Business Lounge");

3. newConcurrentHashSetVehicules.add("Renault Talisman Estate");

4. newConcurrentHashSetVehicules.add("Opel Meriva");

5. newConcurrentHashSetVehicules.add("Nouvelle Peugeot 508");

6. out.println(newConcurrentHashSetVehicules); // [Opel Meriva, Audi A8 L, Nouvelle Peugeot 508, Citroën Space Tourer Business Lounge, Renault Talisman Estate]

IV-J-2 - newConcurrentHashSet(Iterable<? extends E> elements)

Cette méthode possède également une autre variante prenant comme paramètre un Iterable ; cette variante quant à elle nous permet de créer un HashSet directement avec nos véhicules.

newConcurrentHashSet(Iterable<? extends E> elements)

1. final Set<String> newConcurrentHashSetVehiculesAudi = Sets.newConcurrentHashSet(audi);

2. out.println(newConcurrentHashSetVehiculesAudi); // [Audi A4 Berline, Audi A6 Berline TFSI, Audi A5 Sportback, Audi A1 Sportback, Audi Nouvelle A8 L TFSI e, Audi Nouvelle A3 Sportback, Audi A7 Sportback TFSI e]

IV-K - La méthode newCopyOnWriteArraySet

Cette méthode permet de créer une instance vide de CopyOnWriteArraySet.

IV-K-1 - newCopyOnWriteArraySet()

Nous pouvons également créer une instance vide de la classe CopyOnWriteArraySet.

Méthode newCopyOnWriteArraySet()

1. final CopyOnWriteArraySet<String> newCopyOnWriteArraySetVide = Sets.newCopyOnWriteArraySet(); // une instance vide de la classe CopyOnWriteArraySet

2. out.println(newCopyOnWriteArraySetVide);  // []

IV-K-2 - newCopyOnWriteArraySet(Iterable<? extends E> elements)

Une autre signature de cette méthode permet de passer en paramètre un Iterable contenant les éléments donnés.

newCopyOnWriteArraySet(Iterable<? extends E> elements)

1. final CopyOnWriteArraySet<String> newCopyOnWriteArraySetVehiculesAudi = Sets.newCopyOnWriteArraySet(newConcurrentHashSetVehiculesAudi);

2. out.println(newCopyOnWriteArraySetVehiculesAudi); // [Audi A4 Berline, Audi A6 Berline TFSI, Audi A5 Sportback, Audi A1 Sportback, Audi Nouvelle A8 L TFSI e, Audi Nouvelle A3 Sportback, Audi A7 Sportback TFSI e]

IV-L - La méthode newEnumSet

Si nous souhaitons instancier un objet de type EnumSet mutable, nous pouvons utiliser cette méthode en lui passant comme paramètres des éléments Iterables.

Méthode newEnumSet(Iterable<E> iterable, Class<E> elementType)

1. final Iterable<Caracteristique> iterableCaracteristiqueMotrice = ImmutableList.of(Caracteristique.CYLINDREE, Caracteristique.SOUPAPES, Caracteristique.INJECTION, Caracteristique.PUISSANCE);

2. final EnumSet<Caracteristique> enumSetCaracteristiqueVehicules = Sets.newEnumSet(iterableCaracteristiqueMotrice, Caracteristique.class);

3. out.println(enumSetCaracteristiqueVehicules); // [CYLINDREE, PUISSANCE, SOUPAPES, INJECTION]

IV-M - La méthode newHashSet

Cette méthode permet de créer une instance mutable HashSet, initialement vide.

IV-M-1 - newHashSet()

Nous pouvons également créer une instance vide de la classe HashSet.

Méthode newHashSet()

1. final HashSet<String> newHashSet = Sets.newHashSet();

2. out.println(newHashSet); // []

IV-M-2 - newHashSet(E... elements)

Une autre signature de cette méthode permet de passer en paramètres des éléments donnés.

newHashSet(E... elements)

1. final Set<Integer> newHashSetWithElements = Sets.newHashSet(cylindrees);

2. out.println(newHashSetWithElements); // [1300, 1700, 1461, 999, 1560, 3400, 5500, 2398, 1983, 5935]

IV-M-3 - newHashSet(Iterable<? extends E> elements)

Une autre signature de cette méthode permet de passer en paramètre un objet de type Iterable.

Méthode newHashSet(Iterable<? extends E> elements)

1. final Set<String> newHashSetWithIterable = Sets.newHashSet(newConcurrentHashSetVehiculesAudi);

2. out.println(newHashSetWithIterable); // [Audi A4 Berline, Audi A6 Berline TFSI, Audi A5 Sportback, Audi A1 Sportback, Audi Nouvelle A8 L TFSI e, Audi Nouvelle A3 Sportback, Audi A7 Sportback TFSI e]

IV-M-4 - newHashSet(Iterator<? extends E> elements)

Une autre signature de cette méthode permet de passer en paramètre un objet de type Iterator.

Méthode newHashSet(Iterator<? extends E> elements)

1. final Iterator<String> iteratorMitsubishi = mitsubishi.iterator();

2. final Set<String> newHashSetWithIterator = Sets.newHashSet(iteratorMitsubishi);

3. out.println(newHashSetWithIterator); // [Mitsubishi SUV ASX, Mitsu L200, Mitsubishi Eclipse Cross PHEV, Mitsubishi Outlander PHEV, Mitsu Outlander, Mitsubishi Space Star]

IV-N - La méthode newHashSetWithExpectedSize

Nous pouvons également instancier un HashSet d’une taille prédéfinie. Cette opération est triviale.

Méthode newHashSetWithExpectedSize(int expectedSize)

1. final Set<Integer> newHashSetWithExpectedSize = Sets.newHashSetWithExpectedSize(NB_VEHICULES);

2. out.println(newHashSetWithExpectedSize.size()); // 0 car aucun élément n'a encore été ajouté. On peut ajouter un nombre d'éléments > NB_VEHICULES car un HashSet est une structure de données dynamique !

IV-O - La méthode newIdentityHashSet

On peut également créer un Set vide utilisant la fonctionnalité identity pour déterminer l’égalité par comparaison des références d’objets.

Méthode newIdentityHashSet()

1. final Set<String> newIdentityHashSet = Sets.newIdentityHashSet();

2. out.println(newIdentityHashSet); // []

IV-P - La méthode newLinkedHashSet

Cette méthode permet de créer une instance mutable vide d’un LinkedHashSet.

IV-P-1 - newLinkedHashSet()

Nous pouvons également créer une instance vide de la classe LinkedHashSet.

Méthode newLinkedHashSet

1. final Set<String> newLinkedHashSet = Sets.newLinkedHashSet();

2. out.println(newLinkedHashSet); // []

IV-P-2 - newLinkedHashSet(Iterable<? extends E> elements)

Une autre signature de cette méthode permet de passer en paramètre un objet de type Iterable.

Méthode newLinkedHashSet(Iterable<? extends E> elements)

1. final Set<Integer> newLinkedHashSetWithIterable = Sets.newLinkedHashSet(cylindrees);

2. out.println(newLinkedHashSetWithIterable); // // [1300, 1700, 1461, 999, 1560, 3400, 5500, 2398, 1983, 5935]

IV-Q - La méthode newLinkedHashSetWithExpectedSize

Nous pouvons également instancier un LinkedHashSet d’une taille prédéfinie. Cette opération est triviale.

Méthode newLinkedHashSetWithExpectedSize(int expectedSize)

1. final Set<Integer> newLinkedHashSetWithExpectedSize = Sets.newLinkedHashSetWithExpectedSize(NB_VEHICULES);

2. out.println(newLinkedHashSetWithExpectedSize); // []

IV-R - La méthode newTreeSet

Cette méthode permet de créer une instance de TreeSet mutable et vide, triée selon l'ordre de tri naturel de ses éléments.

IV-R-1 - newTreeSet()

Nous pouvons également créer une instance vide de la classe TreeSet.

Méthode newTreeSet()

1. final TreeSet<String> newTreeSet = Sets.newTreeSet();

2. out.println(newTreeSet); // []

IV-R-2 - newTreeSet(Comparator<? super E> comparator)

Une autre signature de cette méthode permet de passer en paramètre un objet de type Comparator.

Méthode newTreeSet(Comparator<? super E> comparator)

1. final Comparator<String> comparator = (String o1, String o2) -> (o2.length() - o1.length());

2. TreeSet<String> newTreeSetWithComparator = Sets.newTreeSet(comparator);

3. out.println(newTreeSetWithComparator); // []

IV-R-3 - newTreeSet(Iterable<? extends E> elements)

Une autre signature de cette méthode permet de passer en paramètre un objet de type Iterable. Nous souhaitons parcourir nos véhicules de la marque Volkswagen sous la forme d’un TreeSet.

Méthode newTreeSet(Iterable<? extends E> elements)

1. final Set<String> newTreeSetWithIterable = Sets.newTreeSet(volkswagen);

2. out.println(newTreeSetWithIterable); // [VW Arteon, VW Caddy, VW Passat, VW Tiguan, Volkswagen Amarok, Volkswagen Golf, Volkswagen Polo, Volkswagen Sharan, Volkswagen T-cross, Volkswagen Vitto]

IV-S - La méthode powerSet

Nous souhaitons renvoyer tous les sous-ensembles possibles d’un ensemble. La méthode powerSet(Set<E> set) permet d’effectuer cette opération. Imaginons que nous voulions tous les sous-ensembles des véhicules de la marque Mitshubishi.

Méthode powerSet(Set<E> set)

1. final Set<Set<String>> powerSet = Sets.powerSet(mitsubishi);

2. out.println(powerSet); // powerSet({Mitsu L200=0, Mitsu Outlander=1, Mitsubishi Eclipse Cross PHEV=2, Mitsubishi Outlander PHEV=3, Mitsubishi SUV ASX=4, Mitsubishi Space Star=5})

IV-T - La méthode subSet

Nous pourrions tout aussi bien renvoyer une vue de la partie d'un ensemble dont les éléments sont contenus dans une plage définie. La méthode subSet(NavigableSet<K> set, Range<K> range) permet d’effectuer cette opération. Nous voulons les véhicules ayant une puissance moteur supérieure à 400.

Méthode subSet(NavigableSet<K> set, Range<K> range)

1. final Range<Integer> puissanceGreaterThan400 = Range.greaterThan(400);

2. final NavigableSet<Integer> subSetPuissanceMoteur = Sets.subSet(puissances, puissanceGreaterThan400);

3. out.println(subSetPuissanceMoteur); // [525, 624, 705, 826]

IV-U - La méthode symmetricDifference

Cette méthode renvoie une vue non modifiable de la différence symétrique de deux ensembles. Nous pouvons l’utiliser par exemple pour visualiser les cylindrées et les puissances moteur de nos véhicules.

Méthode symmetricDifference(Set<? extends E> set1, Set<? extends E> set2)

1. final Sets.SetView<Integer> symmetricDifference = Sets.symmetricDifference(cylindrees, puissances);

2. out.println(symmetricDifference); // [1700, 1461, 1560, 3400, 5500, 2398, 1983, 5935, 178, 224, 285, 314, 347, 525, 624, 705, 826] ==> 999 et 1300 sont absents

IV-V - La méthode synchronizedNavigableSet

Dans un environnement concurrentiel (où de nombreux fils d’exécution ont accès à une donnée en même temps), la méthode synchronizedNavigableSet(NavigableSet<E> navigableSet) permet de renvoyer un ensemble navigable synchronisé soutenu par l'ensemble navigable spécifié.

Méthode synchronizedNavigableSet(NavigableSet<E> navigableSet)

1. final NavigableSet<Integer> synchronizedNavigableSet = Sets.synchronizedNavigableSet(puissances);

2. out.println(synchronizedNavigableSet); // [178, 224, 285, 314, 347, 525, 624, 705, 826, 999, 1300]

IV-W - La méthode toImmutableEnumSet

Imaginons que nous voulions renvoyer un collecteur qui accumulerait les éléments d'entrée dans un nouvel ImmutableSet avec une implémentation spécialisée pour les enums. La méthode toImmutableEnumSet() permet d’effectuer cette opération de façon triviale.

Méthode toImmutableEnumSet()

1. final ImmutableSet<String> immutableEnumSetMoteur = Sets.immutableEnumSet(cylindree, puissance, soupapes, injection);

2. final Supplier<ImmutableSet<String>> collectionFactory = () -> immutableEnumSetMoteur;

3. final Collector<String, ?, ImmutableSet<String>> toImmutableEnumSet = Collectors.toCollection(collectionFactory);

4. out.println(toImmutableEnumSet.supplier().get()); // [CYLINDREE, PUISSANCE, SOUPAPES, INJECTION]

IV-X - La méthode union

Il est également possible de renvoyer une vue non modifiable de l'union de deux ensembles en utilisant la méthode union(Set<? extends E> set1, Set<? extends E> set2). Nous voulons l’union des véhicules des marques Audi et Volkswagen.

Méthode union(Set<? extends E> set1, Set<? extends E> set2)

1. final Sets.SetView<String> unionAudiVolkswagen = Sets.union(audi, volkswagen);

2. out.println(unionAudiVolkswagen); // [Audi A4 Berline, Audi A6 Berline TFSI, Audi A5 Sportback, Audi A1 Sportback, Audi Nouvelle A8 L TFSI e, Audi Nouvelle A3 Sportback, Audi A7 Sportback TFSI e, Volkswagen Amarok, Volkswagen Arteon, Volkswagen Caddy, Volkswagen Golf, Volkswagen Passat, Volkswagen Polo, Volkswagen Sharan, Volkswagen T-cross, Volkswagen Tiguan, Volkswagen Vitto]

IV-Y - La méthode unmodifiableNavigableSet

Pour finir, nous si nous voulons renvoyer une vue non modifiable d’un ensemble navigable spécifié, la méthode unmodifiableNavigableSet(NavigableSet<E> set) permet d’effectuer cette opération.

Méthode unmodifiableNavigableSet(NavigableSet<E> set)

1. final NavigableSet<String> unmodifiableNavigableSetMitsubishi = Sets.unmodifiableNavigableSet(mitsubishi);

2. out.println(unmodifiableNavigableSetMitsubishi); // [Mitsu L200, Mitsu Outlander, Mitsubishi Eclipse Cross PHEV, Mitsubishi Outlander PHEV, Mitsubishi SUV ASX, Mitsubishi Space Star]

V - Conclusion

Nous avons fait le tour des méthodes utilitaires présentes dans la classe Sets. Comme vous avez pu le constater, ces méthodes sont à la fois très simples d’utilisation et particulièrement efficaces. Je vous invite fortement à les implémenter dans vos projets, car elles vous apporteront des avantages sans précédent en matière de productivité.

VI - Remerciements

Je tiens à remercier tout particulièrement escartefigue pour la relecture orthographique de cet article.

Je remercie également la communauté developpez.com qui propose un forum d’entraide d’une richesse fortement appréciable. Sans oublier Mickael Baron qui donne de son temps pour s’occuper de la rubrique Java, RomeoBeni qui a écrit un ouvrage d’une rare simplicité pour apprendre à programmer en Java, mais aussi tous les membres répondant aux questions des débutants en programmation dont je faisais partie il y a quelques années de cela. Merci encore à tous.