Apache Commons Collections與Google Guava

1.概述

在本教程中,我們將比較兩個基於Java的開源庫: Apache CommonsGoogle Guava 。這兩個庫都具有豐富的功能集,主要在集合和I / O區域中具有許多實用程序API。

為簡便起見,在這裡我們僅描述集合框架中的一些最常用的方法以及代碼示例。我們還將看到它們差異的摘要。

此外,我們還收集了一系列文章,以深入探究各種公用資源和Guava實用程序

2.兩個開源庫的簡要歷史

Google Guava是一個Google項目,主要由該組織的工程師開發,儘管該項目現已開源。啟動它的主要動機是將JDK 1.5中引入的泛型包含到Java Collections FrameworkJCF中,並增強其功能。

自創建以來,該庫已擴展了功能,現在包括圖形,函數編程,範圍對象,緩存和String操作。

Apache Commons從Jakarta項目開始,以補充核心Java集合API,並最終成為Apache Software Foundation的項目。多年來,它已擴展為其他各個領域中可重複使用的Java組件的巨大組成部分,包括(但不限於)映像,I / O,加密,緩存,聯網,驗證和對像池。

由於這是一個開源項目,因此來自Apache社區的開發人員不斷添加該庫以擴展其功能。但是,他們非常注意保持向後兼容性

3. Maven依賴

要包括番石榴,我們需要將其依賴項添加到我們的pom.xml

<dependency>

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

 <artifactId>guava</artifactId>

 <version>29.0-jre</version>

 </dependency>

它的最新版本信息可以在Maven上找到。

對於Apache Commons,情況有所不同。根據我們要使用的實用程序,我們必須添加該特定程序。例如,對於集合,我們需要添加:

<dependency>

 <groupId>org.apache.commons</groupId>

 <artifactId>commons-collections4</artifactId>

 <version>4.4</version>

 </dependency>

在我們的代碼示例中,我們將使用commons-collections4

讓我們立即進入有趣的部分!

4.雙向Map

可以通過其鍵訪問的映射以及值稱為雙向映射。 JCF沒有此功能。

讓我們看看我們的兩種技術如何為他們提供的。在這兩種情況下,我們都將以一周中的某幾天為例,以給出其日期的日期作為名稱,反之亦然。

4.1。番石榴的BiMap

Guava提供了一個接口BiMap ,作為雙向地圖。 EnumBiMapEnumHashBiMapHashBiMapImmutableBiMap之一來實例化它

在這裡,我們使用HashBiMap

BiMap<Integer, String> 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 Apache的BidiMap

同樣,Apache為我們提供了其BidiMap接口:

BidiMap<Integer, String> daysOfWeek = new TreeBidiMap<Integer, String>();

在這裡,我們使用TreeBidiMap 。但是,還有其他實現,例如DualHashBidiMapDualTreeBidiMap

要填充它,我們可以像上面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));

 }

在一些簡單的性能測試中, 此雙向映射僅在插入時落後於其Guava副本。它在獲取鍵和值時要快得多

5.將鍵映射到多個值

對於需要將多個鍵映射到不同值的用例,例如水果和蔬菜的雜貨車集合,這兩個庫為我們提供了獨特的解決方案。

5.1 Guava的MultiMap

首先,讓我們看看如何實例化和初始化MultiMap

Multimap<String, String> 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<String> fruits = Arrays.asList("Apple", "Grapes", "Strawberries");

 assertEquals(fruits, groceryCart.get("Fruits"));

 }



 @Test

 public void givenMultiValuedMap_whenVeggiesFetched_thenVeggiesReturned() {

 List<String> veggies = Arrays.asList("Spinach", "Cabbage");

 assertEquals(veggies, groceryCart.get("Vegetables"));

 }

此外, MultiMap使我們能夠從地圖中刪除給定的條目或整個值集

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

 }

如我們所見,在這裡,我們首先從“ Fruits Apple ,然後刪除了整個“ Fruits集中。

5.2。 Apache的MultiValuedMap

再次,讓我們從實例化MultiValuedMap開始:

MultiValuedMap<String, String> groceryCart = new ArrayListValuedHashMap<>();

由於填充它與上一節中看到的相同,因此讓我們快速查看一下用法:

@Test

 public void givenMultiValuedMap_whenFruitsFetched_thenFruitsReturned() {

 List<String> fruits = Arrays.asList("Apple", "Grapes", "Strawberries");

 assertEquals(fruits, groceryCart.get("Fruits"));

 }



 @Test

 public void givenMultiValuedMap_whenVeggiesFetched_thenVeggiesReturned() {

 List<String> veggies = Arrays.asList("Spinach", "Cabbage");

 assertEquals(veggies, groceryCart.get("Vegetables"));

 }

可以看到,它的用法也一樣!

但是,在這種情況下,我們沒有靈活性來刪除單個條目,例如從Fruits. Apple我們只能刪除整個**Fruits** :

@Test

 public void givenMultiValuedMap_whenFuitsRemoved_thenVeggiesPreserved() {

 assertEquals(5, groceryCart.size());



 groceryCart.remove("Fruits");

 assertEquals(2, groceryCart.size());

 }

6.將多個鍵映射到一個值

在這裡,我們將以經度和緯度為例,分別映射到各個城市:

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

現在,我們將看到如何實現這一目標。

6.1。Guava的Table

Guava提供的Table可以滿足上述用例:

Table<String, String, String> cityCoordinates = HashBasedTable.create();

這是我們可以從中得出的一些用法:

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

 }

如我們所見,我們可以獲得行,列和值Set

Table還為我們提供了查詢其行或列的能力

讓我們考慮一個電影桌來演示這一點:

Table<String, String, String> 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");

以下是一些我們可以在movies Table不言自明的示例搜索:

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

 }

但是, Table限制了我們僅將兩個鍵映射到一個值。在Guava中,我們還沒有其他選擇可以將兩個以上的鍵映射到一個值。

6.2。 Apache的MultiKeyMap

回到我們的cityCoordinates示例,這是我們如何使用MultiKeyMap操作它的方法:

@Test

 public void givenCoordinatesMultiKeyMap_whenQueried_thenOK() {

 MultiKeyMap<String, String> cityCoordinates = new MultiKeyMap<String, String>();



 // 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());

 }

從上面的代碼片段可以看出,要得出與Guava的Table相同的斷言,我們必須遍歷MultiKeyMap

但是, MultiKeyMap還提供了將兩個以上的鍵映射到一個值的可能性。例如,它使我們能夠將星期幾映射為工作日或週末:

@Test

 public void givenDaysMultiKeyMap_whenFetched_thenOK() {

 days = new MultiKeyMap<String, String>();

 days.put("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Weekday");

 days.put("Saturday", "Sunday", "Weekend");



 assertFalse(days.get("Saturday", "Sunday").equals("Weekday"));

 }

7. Apache Commons Collections與Google Guava的比較

根據其工程師的說法, Google Guava誕生於使用該庫中的泛型的需要,而Apache Commons不提供該泛型。它也遵循對T恤的collection API要求。另一個主要優點是它正在積極開發中,並且經常發布新版本。

但是,在從集合中獲取值的同時,Apache在性能方面具有優勢。不過,就插入時間而言,番石榴仍然佔據了蛋糕的位置。

儘管我們僅比較了代碼示例中的collection API,但與Guava相比,Apache Commons整體上提供了更多的功能

8.結論

在本教程中,我們比較了Apache Commons和Google Guava提供的某些功能,特別是在集合框架方面。

在這裡,我們只是簡單介紹了兩個庫必須提供的內容。

而且,這不是“或非”比較。正如我們的代碼示例所演示的,這兩者都有各自獨有的功能,並且在某些情況下兩者可以共存