Collections Apache Commons vs Google Guava

1. Vue d'ensemble

Dans ce didacticiel, nous comparerons deux bibliothèques open source basées sur Java: Apache Commons et Google Guava . Les deux bibliothèques ont un riche ensemble de fonctionnalités avec de nombreuses API utilitaires principalement dans les collections et la zone d'E / S.

Par souci de concision, nous ne décrirons ici que quelques-uns des plus couramment utilisés dans le cadre des collections avec des exemples de code. Nous verrons également un résumé de leurs différences.

De plus, nous avons une collection d'articles pour une plongée approfondie dans divers communs et utilitaires de Guava .

2. Bref historique des deux bibliothèques

Google Guava est un projet Google, principalement développé par les ingénieurs de l'organisation, bien qu'il soit désormais open-source. La principale motivation pour le démarrer était d'inclure les génériques introduits dans JDK 1.5 dans Java Collections Framework , ou JCF, et d'améliorer sa capacité.

Depuis sa création, la bibliothèque a étendu ses capacités et comprend désormais des graphiques, une programmation fonctionnelle, des objets de plage, la mise en cache et la manipulation de chaînes .

Apache Commons a commencé comme un projet de Jakarta pour compléter l'API de base des collections Java et est finalement devenu un projet de l'Apache Software Foundation. Au fil des ans, il s'est étendu à un vaste répertoire de composants Java réutilisables dans divers autres domaines, y compris (mais sans s'y limiter) l'imagerie, les E / S, la cryptographie, la mise en cache, la mise en réseau, la validation et la mise en commun d'objets.

Comme il s'agit d'un projet open source, les développeurs de la communauté Apache continuent d'ajouter à cette bibliothèque pour étendre ses capacités. Cependant, ils prennent grand soin de maintenir la compatibilité ascendante .

3. Dépendance de Maven

Pour inclure Guava, nous devons ajouter sa dépendance à notre pom.xml :

 com.google.guava guava 29.0-jre 

Ses dernières informations sur la version peuvent être trouvées sur Maven.

Pour Apache Commons, c'est un peu différent. En fonction de l'utilitaire que nous voulons utiliser, nous devons ajouter celui-là. Par exemple, pour les collections, nous devons ajouter:

 org.apache.commons commons-collections4 4.4 

Dans nos exemples de code, nous utiliserons commons-collections4 .

Passons maintenant à la partie amusante!

4. Cartes bidirectionnelles

Les cartes accessibles par leurs clés, ainsi que par leurs valeurs, sont appelées cartes bidirectionnelles. JCF n'a pas cette fonctionnalité.

Voyons comment nos deux technologies les offrent. Dans les deux cas, nous prendrons un exemple de jours de la semaine pour obtenir le nom du jour compte tenu de son numéro et vice-versa.

4.1. BiMap de la goyave

Guava propose une interface - BiMap , sous forme de carte bidirectionnelle. Il peut être instancié avec l'une de ses implémentations EnumBiMap , EnumHashBiMap , HashBiMap ou ImmutableBiMap .

Ici, nous utilisons HashBiMap :

BiMap daysOfWeek = HashBiMap.create();

Le peupler est similaire à n'importe quelle carte en Java:

daysOfWeek.put(1, "Monday"); daysOfWeek.put(2, "Tuesday"); daysOfWeek.put(3, "Wednesday"); daysOfWeek.put(4, "Thursday"); daysOfWeek.put(5, "Friday"); daysOfWeek.put(6, "Saturday"); daysOfWeek.put(7, "Sunday");

Et voici quelques tests JUnit pour prouver le concept:

@Test public void givenBiMap_whenValue_thenKeyReturned() { assertEquals(Integer.valueOf(7), daysOfWeek.inverse().get("Sunday")); } @Test public void givenBiMap_whenKey_thenValueReturned() { assertEquals("Tuesday", daysOfWeek.get(2)); }

4.2. BidiMap d'Apache

De même, Apache nous fournit son interface BidiMap :

BidiMap daysOfWeek = new TreeBidiMap();

Ici, nous utilisons TreeBidiMap . Cependant, il y a d' autres implémentations, sommes telles que DualHashBidiMap et DualTreeBidiMap ainsi .

Pour le remplir, nous pouvons mettre les valeurs comme nous l'avons fait pour BiMap ci-dessus.

Son utilisation est également assez similaire:

@Test public void givenBidiMap_whenValue_thenKeyReturned() { assertEquals(Integer.valueOf(7), daysOfWeek.inverseBidiMap().get("Sunday")); } @Test public void givenBidiMap_whenKey_thenValueReturned() { assertEquals("Tuesday", daysOfWeek.get(2)); }

En quelques tests de performances simples, cette carte bidirectionnelle a pris du retard par rapport à son homologue Guava uniquement dans les insertions. Il était beaucoup plus rapide de récupérer les clés ainsi que les valeurs .

5. Mappez les clés sur plusieurs valeurs

Pour un cas d'utilisation où nous voudrions mapper plusieurs clés à différentes valeurs, comme une collection de paniers d'épicerie pour les fruits et légumes, les deux bibliothèques nous offrent des solutions uniques.

5.1. MultiMap de goyave

Voyons d'abord comment instancier et initialiser MultiMap :

Multimap groceryCart = ArrayListMultimap.create(); groceryCart.put("Fruits", "Apple"); groceryCart.put("Fruits", "Grapes"); groceryCart.put("Fruits", "Strawberries"); groceryCart.put("Vegetables", "Spinach"); groceryCart.put("Vegetables", "Cabbage");

Ensuite, nous utiliserons quelques tests JUnit pour le voir en action:

@Test public void givenMultiValuedMap_whenFruitsFetched_thenFruitsReturned() { List fruits = Arrays.asList("Apple", "Grapes", "Strawberries"); assertEquals(fruits, groceryCart.get("Fruits")); } @Test public void givenMultiValuedMap_whenVeggiesFetched_thenVeggiesReturned() { List veggies = Arrays.asList("Spinach", "Cabbage"); assertEquals(veggies, groceryCart.get("Vegetables")); } 

De plus, MultiMap nous donne la possibilité de supprimer une entrée donnée ou un ensemble complet de valeurs de la carte :

@Test public void givenMultiValuedMap_whenFuitsRemoved_thenVeggiesPreserved() { assertEquals(5, groceryCart.size()); groceryCart.remove("Fruits", "Apple"); assertEquals(4, groceryCart.size()); groceryCart.removeAll("Fruits"); assertEquals(2, groceryCart.size()); }

Comme nous pouvons le voir, nous avons d'abord supprimé Apple de l' ensemble Fruits , puis nous avons supprimé l'ensemble de l'ensemble Fruits .

5.2. MultiValuedMap d'Apache

Encore une fois, commençons par instancier une MultiValuedMap :

MultiValuedMap groceryCart = new ArrayListValuedHashMap();

Étant donné que son remplissage est le même que celui que nous avons vu dans la section précédente, regardons rapidement l'utilisation:

@Test public void givenMultiValuedMap_whenFruitsFetched_thenFruitsReturned() { List fruits = Arrays.asList("Apple", "Grapes", "Strawberries"); assertEquals(fruits, groceryCart.get("Fruits")); } @Test public void givenMultiValuedMap_whenVeggiesFetched_thenVeggiesReturned() { List veggies = Arrays.asList("Spinach", "Cabbage"); assertEquals(veggies, groceryCart.get("Vegetables")); }

Comme on peut le voir, son utilisation est également la même!

Cependant, dans ce cas, nous n'avons pas la possibilité de supprimer une seule entrée, telle que Apple de Fruits. Nous ne pouvons supprimer que l'ensemble complet de Fruits :

@Test public void givenMultiValuedMap_whenFuitsRemoved_thenVeggiesPreserved() { assertEquals(5, groceryCart.size()); groceryCart.remove("Fruits"); assertEquals(2, groceryCart.size()); }

6. Mapper plusieurs clés à une valeur

Ici, nous allons prendre un exemple de latitudes et longitudes à cartographier aux villes respectives:

cityCoordinates.put("40.7128° N", "74.0060° W", "New York"); cityCoordinates.put("48.8566° N", "2.3522° E", "Paris"); cityCoordinates.put("19.0760° N", "72.8777° E", "Mumbai");

Maintenant, nous allons voir comment y parvenir.

6.1. Table de goyave

Guava propose sa table qui satisfait le cas d'utilisation ci-dessus:

Table cityCoordinates = HashBasedTable.create();

Et voici quelques usages que nous pouvons en tirer:

@Test public void givenCoordinatesTable_whenFetched_thenOK() { List expectedLongitudes = Arrays.asList("74.0060° W", "2.3522° E", "72.8777° E"); assertArrayEquals(expectedLongitudes.toArray(), cityCoordinates.columnKeySet().toArray()); List expectedCities = Arrays.asList("New York", "Paris", "Mumbai"); assertArrayEquals(expectedCities.toArray(), cityCoordinates.values().toArray()); assertTrue(cityCoordinates.rowKeySet().contains("48.8566° N")); }

Comme nous pouvons le voir, nous pouvons obtenir un ensemble vue des lignes, des colonnes et des valeurs.

Table nous offre également la possibilité d'interroger ses lignes ou colonnes .

Considérons une table de film pour démontrer ceci:

Table movies = HashBasedTable.create(); movies.put("Tom Hanks", "Meg Ryan", "You've Got Mail"); movies.put("Tom Hanks", "Catherine Zeta-Jones", "The Terminal"); movies.put("Bradley Cooper", "Lady Gaga", "A Star is Born"); movies.put("Keenu Reaves", "Sandra Bullock", "Speed"); movies.put("Tom Hanks", "Sandra Bullock", "Extremely Loud & Incredibly Close");

Et voici quelques exemples de recherches explicites que nous pouvons faire sur notre tableau de films :

@Test public void givenMoviesTable_whenFetched_thenOK() { assertEquals(3, movies.row("Tom Hanks").size()); assertEquals(2, movies.column("Sandra Bullock").size()); assertEquals("A Star is Born", movies.get("Bradley Cooper", "Lady Gaga")); assertTrue(movies.containsValue("Speed")); }

However, Table limits us to map only two keys to a value. We don't have an alternative as yet in Guava to map more than two keys to a single value.

6.2. Apache's MultiKeyMap

Coming back to our cityCoordinates example, here's how we can manipulate it using MultiKeyMap:

@Test public void givenCoordinatesMultiKeyMap_whenQueried_thenOK() { MultiKeyMap cityCoordinates = new MultiKeyMap(); // populate with keys and values as shown previously List expectedLongitudes = Arrays.asList("72.8777° E", "2.3522° E", "74.0060° W"); List longitudes = new ArrayList(); cityCoordinates.forEach((key, value) -> { longitudes.add(key.getKey(1)); }); assertArrayEquals(expectedLongitudes.toArray(), longitudes.toArray()); List expectedCities = Arrays.asList("Mumbai", "Paris", "New York"); List cities = new ArrayList(); cityCoordinates.forEach((key, value) -> { cities.add(value); }); assertArrayEquals(expectedCities.toArray(), cities.toArray()); }

As we can see from the above code snippet, to arrive at the same assertions as for Guava's Table, we had to iterate over the MultiKeyMap.

However, MultiKeyMap also offers the possibility to map more than two keys to a value. For example, it gives us the ability to map days of the week as weekdays or weekends:

@Test public void givenDaysMultiKeyMap_whenFetched_thenOK() { days = new MultiKeyMap(); days.put("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Weekday"); days.put("Saturday", "Sunday", "Weekend"); assertFalse(days.get("Saturday", "Sunday").equals("Weekday")); }

7. Apache Commons Collections vs. Google Guava

As per its engineers, Google Guava was born out of the need to use generics in the library, which Apache Commons didn't offer. It also follows the collections API requirements to the tee. Another major advantage is that it's in active development with new releases coming out frequently.

However, Apache offers an edge when it comes to performance while fetching a value from a collection. Guava still takes the cake though, in terms of insertion times.

Although we compared only the collections APIs in our code samples, Apache Commons as a whole offers a much bigger gamut of features as compared to Guava.

8. Conclusion

In this tutorial, we compared some of the functionality offered by Apache Commons and Google Guava, specifically in the area of the collections framework.

Ici, nous n'avons fait qu'effleurer la surface de ce que les deux bibliothèques ont à offrir.

De plus, ce n'est pas une comparaison entre les deux. Comme nos exemples de code l'ont démontré, il existe des fonctionnalités uniques à chacun des deux et il peut y avoir des situations où les deux peuvent coexister .

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