Apache Commons Collections gegen Google Guava

1. Übersicht

In diesem Tutorial werden zwei Java-basierte Open Source-Bibliotheken verglichen : Apache Commons und Google Guava . Beide Bibliotheken verfügen über umfangreiche Funktionen mit vielen Dienstprogramm-APIs, hauptsächlich im Bereich Sammlungen und E / A.

Der Kürze halber beschreiben wir hier nur eine Handvoll der am häufigsten verwendeten aus dem Sammlungsframework zusammen mit Codebeispielen. Wir werden auch eine Zusammenfassung ihrer Unterschiede sehen.

Zusätzlich haben wir eine Sammlung von Artikeln für einen tiefen Einblick in verschiedene Commons und Guava-Dienstprogramme .

2. Eine kurze Geschichte der beiden Bibliotheken

Google Guava ist ein Google-Projekt, das hauptsächlich von den Ingenieuren des Unternehmens entwickelt wurde, obwohl es jetzt Open-Source ist. Die Hauptmotivation für den Start bestand darin, in JDK 1.5 eingeführte Generika in das Java Collections Framework (JCF) aufzunehmen und seine Funktionen zu verbessern.

Seit ihrer Gründung hat die Bibliothek ihre Funktionen erweitert und umfasst nun Diagramme, funktionale Programmierung, Bereichsobjekte, Caching und String- Manipulation.

Apache Commons wurde als Jakarta-Projekt zur Ergänzung der Java-Kernsammlungs-API gestartet und schließlich zu einem Projekt der Apache Software Foundation. Im Laufe der Jahre hat es sich zu einem riesigen Repertoire wiederverwendbarer Java-Komponenten in verschiedenen anderen Bereichen erweitert, darunter (ohne darauf beschränkt zu sein) Imaging, E / A, Kryptografie, Caching, Netzwerk, Validierung und Objektpooling.

Da es sich um ein Open-Source-Projekt handelt, erweitern Entwickler aus der Apache-Community diese Bibliothek ständig, um ihre Funktionen zu erweitern. Sie achten jedoch sehr darauf, die Abwärtskompatibilität aufrechtzuerhalten .

3. Maven-Abhängigkeit

Um Guava einzuschließen, müssen wir seine Abhängigkeit zu unserer pom.xml hinzufügen :

 com.google.guava guava 29.0-jre 

Die neuesten Versionsinformationen finden Sie auf Maven.

Für Apache Commons ist das etwas anders. Abhängig von dem Dienstprogramm, das wir verwenden möchten, müssen wir dieses spezielle hinzufügen. Für Sammlungen müssen wir beispielsweise Folgendes hinzufügen:

 org.apache.commons commons-collections4 4.4 

In unseren Codebeispielen verwenden wir commons-collection4 .

Lass uns jetzt in den lustigen Teil springen!

4. Bidirektionale Karten

Karten, auf die über ihre Schlüssel zugegriffen werden kann, sowie Werte werden als bidirektionale Karten bezeichnet. JCF verfügt nicht über diese Funktion.

Mal sehen, wie unsere beiden Technologien sie bieten. In beiden Fällen nehmen wir ein Beispiel für Wochentage, um den Namen des Tages anhand seiner Nummer zu erhalten und umgekehrt.

4.1. Guava ist bimap

Guave bietet eine Schnittstelle - BiMap - als bidirektionale Karte. Es kann mit einer seiner Implementierungen EnumBiMap , EnumHashBiMap , HashBiMap oder ImmutableBiMap instanziiert werden .

Hier verwenden wir HashBiMap :

BiMap daysOfWeek = HashBiMap.create();

Das Auffüllen ähnelt jeder Karte in 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");

Und hier sind einige JUnit-Tests, um das Konzept zu beweisen:

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

Ebenso stellt Apache uns seine BidiMap- Oberfläche zur Verfügung:

BidiMap daysOfWeek = new TreeBidiMap();

Hier verwenden wir TreeBidiMap . Es gibt jedoch sind andere Implementierungen, wie DualHashBidiMap und DualTreeBidiMap auch .

Um es zu füllen , können wir die Werte wie oben für BiMap setzen .

Die Verwendung ist auch ziemlich ähnlich:

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

In einigen einfachen Leistungstests blieb diese bidirektionale Karte nur beim Einfügen hinter ihrem Guaven-Gegenstück zurück. Das Abrufen von Schlüsseln und Werten war viel schneller .

5. Ordnen Sie Schlüssel mehreren Werten zu

Für einen Anwendungsfall, in dem wir mehrere Schlüssel unterschiedlichen Werten zuordnen möchten, z. B. eine Sammlung von Einkaufswagen für Obst und Gemüse, bieten uns die beiden Bibliotheken einzigartige Lösungen.

5.1. Guava des MultiMap

Lassen Sie uns zunächst sehen, wie Sie MultiMap instanziieren und initialisieren :

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

Dann werden wir einige JUnit-Tests verwenden, um es in Aktion zu sehen:

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

Darüber hinaus können wir mit MultiMap einen bestimmten Eintrag oder einen ganzen Satz von Werten aus der Karte entfernen :

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

Wie wir sehen können, haben wir hier zuerst Apple aus dem Fruchtset und dann das gesamte Fruchtset entfernt .

5.2. Apaches MultiValuedMap

Beginnen wir erneut mit der Instanziierung einer MultiValuedMap :

MultiValuedMap groceryCart = new ArrayListValuedHashMap();

Da das Auffüllen dasselbe ist wie im vorherigen Abschnitt, schauen wir uns kurz die Verwendung an:

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

Wie wir sehen können, ist seine Verwendung auch die gleiche!

In diesem Fall haben wir jedoch nicht die Flexibilität, einen einzelnen Eintrag wie Apple aus Fruits zu entfernen . Wir können nur den gesamten Satz Früchte entfernen :

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

6. Ordnen Sie einem Wert mehrere Schlüssel zu

Hier nehmen wir ein Beispiel für Breiten- und Längengrade, die den jeweiligen Städten zugeordnet werden sollen:

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

Jetzt werden wir sehen, wie dies erreicht werden kann.

6.1. Guava der Tabelle

Guava bietet eine Tabelle an , die den oben genannten Anwendungsfall erfüllt:

Table cityCoordinates = HashBasedTable.create();

Und hier sind einige Verwendungen, die wir daraus ableiten können:

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

Wie wir sehen können, können wir eine Set- Ansicht der Zeilen, Spalten und Werte erhalten.

Die Tabelle bietet uns auch die Möglichkeit, ihre Zeilen oder Spalten abzufragen .

Betrachten wir eine Filmtabelle, um dies zu demonstrieren:

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

Und hier sind einige Beispiele, selbsterklärend sucht , dass wir auf unserer tun können Filme Tabelle :

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

Hier haben wir lediglich die Oberfläche des Angebots der beiden Bibliotheken zerkratzt.

Darüber hinaus ist es kein Entweder-Oder-Vergleich. Wie unsere Codebeispiele gezeigt haben, gibt es Funktionen, die für jede der beiden Funktionen einzigartig sind, und es kann Situationen geben, in denen beide nebeneinander existieren können .

Wie immer ist der Quellcode über GitHub verfügbar.