Колекции Apache Commons срещу Google Guava

1. Общ преглед

В този урок ще сравним две базирани на Java библиотеки с отворен код: Apache Commons и Google Guava . И двете библиотеки имат богат набор от функции с много приложни програмни интерфейси (API), главно в колекциите и I / O зоната.

За краткост тук ще опишем само шепа от най-често използваните от рамката на колекциите, заедно с примерни кодове. Ще видим и обобщение на разликите им.

Освен това имаме колекция от статии за задълбочено потапяне в различни общи и помощни програми на Guava .

2. Кратка история на двете библиотеки

Google Guava е проект на Google, разработен главно от инженерите на организацията, въпреки че сега е с отворен код. В основния мотив, за да започнете е да се включат генеричните лекарства, въведени в JDK 1.5 в Java Collections Framework или JCF и увеличи неговата способност.

От самото си създаване библиотеката е разширила възможностите си и вече включва графики, функционално програмиране, обект на диапазон, кеширане и манипулиране на низове .

Apache Commons стартира като проект в Джакарта за допълване на основния API на Java колекции и в крайна сметка се превърна в проект на Apache Software Foundation. През годините той се разшири в обширен репертоар от многократно използвани Java компоненти в различни други области, включително (но не само) изображения, I / O, криптография, кеширане, мрежи, валидиране и обединяване на обекти.

Тъй като това е проект с отворен код, разработчиците от общността на Apache продължават да добавят към тази библиотека, за да разширят възможностите си. Въпреки това, те се полагат специални грижи за поддържане на обратна съвместимост .

3. Зависимост на Maven

За да включим Guava, трябва да добавим зависимостта му към нашия pom.xml :

 com.google.guava guava 29.0-jre 

Информацията за последната версия може да бъде намерена на Maven.

За Apache Commons е малко по-различно. В зависимост от помощната програма, която искаме да използваме, трябва да добавим тази конкретна. Например за колекции трябва да добавим:

 org.apache.commons commons-collections4 4.4 

В нашите примерни кодове ще използваме commons-collection4 .

Нека да преминем към забавната част сега!

4. Двупосочни карти

Картите, които могат да бъдат достъпни чрез техните ключове, както и стойностите, са известни като двупосочни карти. JCF няма тази функция.

Нека да видим как нашите две технологии ги предлагат. И в двата случая ще вземем пример за дни от седмицата, за да получим името на деня предвид неговия номер и обратно.

4.1. BiMap на Guava

Guava предлага интерфейс - BiMap , като двупосочна карта. Той може да бъде създаден с една от неговите реализации EnumBiMap , EnumHashBiMap , HashBiMap или ImmutableBiMap .

Тук използваме HashBiMap :

BiMap daysOfWeek = HashBiMap.create();

Попълването му е подобно на всяка карта в 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");

И ето няколко теста JUnit за доказване на концепцията:

@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 на Apache

По подобен начин Apache ни предоставя своя интерфейс BidiMap :

BidiMap daysOfWeek = new TreeBidiMap();

Тук използваме TreeBidiMap . Въпреки това, има си други приложения, като например DualHashBidiMap и DualTreeBidiMap както и .

За да го попълним , можем да поставим стойностите, както направихме за BiMap по-горе.

Използването му също е доста подобно:

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

В няколко прости теста за производителност, тази двупосочна карта изоставаше от колегата си в Гуава само при вмъкване. Беше много по-бързо при извличането на ключове, както и на стойности .

5. Съпоставете ключовете на множество стойности

За случай на употреба, в който бихме искали да съпоставим множество ключове на различни стойности, като колекция от колички за хранителни стоки за плодове и зеленчуци, двете библиотеки ни предлагат уникални решения.

5.1. MultiMap на Guava

Първо, нека видим как да създадем екземпляр и да инициализираме 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");

След това ще използваме няколко теста JUnit, за да го видим в действие:

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

Тук просто надраскахме повърхността на това, което двете библиотеки могат да предложат.

Нещо повече, това не е или-или сравнение. Както демонстрираха нашите примерни кодове, има характеристики, уникални за всеки от двете, и може да има ситуации, при които и двете да съществуват едновременно .

Както винаги, изходният код е достъпен в GitHub.