Colecciones Apache Commons vs Google Guava

1. Información general

En este tutorial, compararemos dos bibliotecas de código abierto basadas en Java: Apache Commons y Google Guava . Ambas bibliotecas tienen un rico conjunto de características con muchas API de utilidades principalmente en el área de colecciones y E / S.

En aras de la brevedad, aquí solo describiremos algunos de los más utilizados del marco de colecciones junto con ejemplos de código. También veremos un resumen de sus diferencias.

Además, tenemos una colección de artículos para una inmersión profunda en varios bienes comunes y utilidades de Guava .

2. Una breve historia de las dos bibliotecas

Google Guava es un proyecto de Google, desarrollado principalmente por los ingenieros de la organización, aunque ahora es de código abierto. La principal motivación para comenzar fue incluir los genéricos introducidos en JDK 1.5 en Java Collections Framework , o JCF, y mejorar su capacidad.

Desde su inicio, la biblioteca ha ampliado sus capacidades y ahora incluye gráficos, programación funcional, objetos de rango, almacenamiento en caché y manipulación de cadenas .

Apache Commons comenzó como un proyecto de Yakarta para complementar la API principal de colecciones de Java y finalmente se convirtió en un proyecto de Apache Software Foundation. A lo largo de los años, se ha expandido a un vasto repertorio de componentes Java reutilizables en varias otras áreas, que incluyen (pero no se limitan a) imágenes, E / S, criptografía, almacenamiento en caché, redes, validación y agrupación de objetos.

Como se trata de un proyecto de código abierto, los desarrolladores de la comunidad Apache siguen agregando a esta biblioteca para expandir sus capacidades. Sin embargo, tienen mucho cuidado de mantener la compatibilidad con versiones anteriores .

3. Dependencia de Maven

Para incluir Guava, necesitamos agregar su dependencia a nuestro pom.xml :

 com.google.guava guava 29.0-jre 

La información de la última versión se puede encontrar en Maven.

Para Apache Commons, es un poco diferente. Dependiendo de la utilidad que queramos usar, tenemos que agregar esa en particular. Por ejemplo, para las colecciones, necesitamos agregar:

 org.apache.commons commons-collections4 4.4 

En nuestros ejemplos de código, usaremos commons-collections4 .

¡Pasemos a la parte divertida ahora!

4. Mapas bidireccionales

Los mapas a los que se puede acceder mediante sus claves, así como sus valores, se conocen como mapas bidireccionales. JCF no tiene esta función.

Veamos cómo las ofrecen nuestras dos tecnologías. En ambos casos, tomaremos un ejemplo de días de la semana para obtener el nombre del día dado su número y viceversa.

4.1. BiMap de Guayaba

Guava ofrece una interfaz, BiMap , como un mapa bidireccional. Se pueden crear instancias con una de sus implementaciones EnumBiMap , EnumHashBiMap , HashBiMap o ImmutableBiMap .

Aquí estamos usando HashBiMap :

BiMap daysOfWeek = HashBiMap.create();

Poblarlo es similar a cualquier mapa 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");

Y aquí hay algunas pruebas de JUnit para probar el concepto:

@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 de Apache

Del mismo modo, Apache nos proporciona su interfaz BidiMap :

BidiMap daysOfWeek = new TreeBidiMap();

Aquí estamos usando TreeBidiMap . Sin embargo, existen otras implementaciones, como DualHashBidiMap y DualTreeBidiMap también .

Para completarlo, podemos poner los valores como hicimos para BiMap arriba.

Su uso también es bastante similar:

@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 unas pocas pruebas de rendimiento simples, este mapa bidireccional se quedó atrás de su contraparte de Guava solo en inserciones. Fue mucho más rápido en la obtención de claves y valores .

5. Asignar claves a varios valores

Para un caso de uso en el que quisiéramos asignar varias claves a diferentes valores, como una colección de carrito de supermercado para frutas y verduras, las dos bibliotecas nos ofrecen soluciones únicas.

5.1. MultiMap de Guayaba

Primero, veamos cómo instanciar e inicializar 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");

Luego, usaremos un par de pruebas JUnit para verlo en acción:

@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")); } 

Additionally, MultiMap gives us the ability to remove a given entry or an entire set of values from the map:

@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()); }

As we can see, here we first removed Apple from the Fruits set and then removed the entire Fruits set.

5.2. Apache's MultiValuedMap

Again, let's begin with instantiating a MultiValuedMap:

MultiValuedMap groceryCart = new ArrayListValuedHashMap();

Since populating it is the same as we saw in the previous section, let's quickly look at the usage:

@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")); }

As we can see, its usage is also the same!

However, in this case, we don't have the flexibility to remove a single entry, such as Apple from Fruits.We can only remove the entire set of Fruits:

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

6. Map Multiple Keys to One Value

Here, we'll take an example of latitudes and longitudes to be mapped to respective cities:

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");

Now, we'll see how to achieve this.

6.1. Guava's Table

Guava offers its Table that satisfies the above use case:

Table cityCoordinates = HashBasedTable.create();

And here are some usages we can derive out of it:

@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")); }

As we can see, we can get a Set view of the rows, columns, and values.

Table also offers us the ability to query its rows or columns.

Let's consider a movie table to demonstrate this:

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");

And here are some sample, self-explanatory searches that we can do on our moviesTable:

@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.

Aquí, simplemente arañamos la superficie de lo que las dos bibliotecas tienen para ofrecer.

Además, no es una comparación. Como demostraron nuestros ejemplos de código, existen características únicas para cada uno de los dos, y puede haber situaciones en las que ambos coexistan .

Como siempre, el código fuente está disponible en GitHub.