Apache Commons Collections vs Google Guava – Apache Commons Collections vs Google Guava

最后修改: 2020年 11月 1日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll compare two Java-based open source libraries: Apache Commons and Google Guava. Both libraries have a rich feature set with lots of utility APIs majorly in the collections and I/O area.

在本教程中,我们将比较两个基于Java的开源库。Apache CommonsGoogle Guava。这两个库都有丰富的功能集,有很多实用的API,主要是在集合和I/O领域。

For brevity, here we’ll only describe a handful of the most commonly used ones from the collections framework along with code samples. We’ll also see a summary of their differences.

为了简洁起见,在这里我们只描述集合框架中最常用的几个,并附上代码样本。我们还将看到它们之间的区别总结。

Additionally, we have a collection of articles for a deep dive into various commons and Guava utilities.

此外,我们还收集了一些文章,以深入了解各种commonsGuava实用程序

2. A Brief History of the Two Libraries

2.两所图书馆的简史

Google Guava is a Google project, mainly developed by the organization’s engineers, although it’s been open-sourced now. The main motivation to start it was to include generics introduced in JDK 1.5 into Java Collections Framework, or JCF, and enhance its capability.

Google Guava是一个Google项目,主要由该组织的工程师开发,尽管它现在已经开源了。启动它的主要动机是将JDK 1.5中引入的泛型纳入Java Collections Framework,即JCF,并增强其能力。

Since its inception, the library has expanded its capabilities and now includes graphs, functional programming, range objects, caching, and String manipulation.

自成立以来,该库扩大了其功能,现在包括图形、函数式编程、范围对象、缓存和String操作。

Apache Commons started as a Jakarta project to supplement the core Java collections API and eventually became a project of the Apache Software Foundation. Over the years, it has expanded into a vast repertoire of reusable Java components in various other areas, including (but not limited to) imaging, I/O, cryptography, caching, networking, validation, and object pooling.

Apache Commons最初是一个Jakarta项目,用于补充核心Java集合API,最终成为Apache软件基金会的一个项目。多年来,它已经扩展为一个庞大的可重用的Java组件库,包括(但不限于)成像、I/O、加密、缓存、网络、验证和对象池等其他领域。

As this is an open-source project, developers from the Apache community keep adding to this library to expand its capabilities. However, they take great care to maintain backward compatibility.

由于这是一个开源项目,来自Apache社区的开发者不断地添加到这个库中以扩展其功能。然而,他们非常注意保持向后的兼容性

3. Maven Dependency

3.Maven的依赖性

To include Guava, we need to add its dependency to our pom.xml:

为了包括Guava,我们需要把它的依赖性添加到我们的pom.xml中。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

It’s latest version information can be found on Maven.

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

For Apache Commons, it’s a bit different. Depending on the utility we want to use, we have to add that particular one. For example, for collections, we need to add:

对于Apache Commons,情况有点不同。根据我们想要使用的工具,我们必须添加那个特定的工具。例如,对于集合,我们需要添加。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

In our code samples, we’ll be using commons-collections4.

在我们的代码示例中,我们将使用commons-collections4

Let’s jump into the fun part now!

现在让我们跳入有趣的部分吧!

4. Bi-directional Maps

4.双向地图

Maps that can be accessed by their keys, as well as values, are known as bi-directional maps. JCF does not have this feature.

可以通过键以及值访问的地图被称为双向地图。JCF不具备这种功能。

Let’s see how our two technologies offer them. In both cases, we’ll take an example of days of the week to get the name of the day given its number and vice-versa.

让我们看看我们的两种技术如何提供它们。在这两种情况下,我们将以一周中的几天为例,根据其数字得到一天的名称,反之亦然。

4.1. Guava’s BiMap

4.1.瓜娃的生物地图

Guava offers an interface – BiMap, as a bi-directional map. It can be instantiated with one of its implementations EnumBiMap, EnumHashBiMap, HashBiMap, or ImmutableBiMap.

Guava提供了一个接口–BiMap,作为一个双向的地图。它可以通过其实现之一EnumBiMapEnumHashBiMapHashBiMapImmutableBiMap进行实例化。

Here we’re using HashBiMap:

这里我们使用HashBiMap

BiMap<Integer, String> daysOfWeek = HashBiMap.create();

Populating it is similar to any map in Java:

填充它类似于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");

And here are some JUnit tests to prove the concept:

这里有一些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’s BidiMap

4.2.Apache的BidiMap

Similarly, Apache provides us with its BidiMap interface:

同样地,Apache为我们提供了BidiMap接口。

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

Here we’re using TreeBidiMap. However, there’re other implementations, such as DualHashBidiMap and DualTreeBidiMap as well.

这里我们使用TreeBidiMap。然而,还有其他的实现,比如DualHashBidiMapDualTreeBidiMap也是如此

To populate it, we can put the values as we did for BiMap above.

为了填充它,我们可以像上面为BiMap所做的那样,把值放进去。

Its usage is also pretty 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));
}

In a few simple performance tests, this bi-directional map lagged behind its Guava counterpart only in insertions. It was much faster in fetching keys as well as values.

在一些简单的性能测试中,这种双向地图 仅在插入方面落后于其Guava 对应程序。它在获取键和值方面要快得多

5. Map Keys to Multiple Values

5.将钥匙映射到多个值

For a use case where we’d want to map multiple keys to different values, such as a grocery cart collection for fruits and vegetables, the two libraries offer us unique solutions.

对于一个我们想把多个键映射到不同的值的用例,比如一个水果和蔬菜的杂货车集合,这两个库为我们提供了独特的解决方案。

5.1. Guava’s MultiMap

5.1.Guava的MultiMap

First, let’s see how to instantiate and initialize 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");

Then, we’ll use a couple of JUnit tests to see it in action:

然后,我们将使用几个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"));
}

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

此外,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());
}

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

5.2.Apache的MultiValuedMap

Again, let’s begin with instantiating a MultiValuedMap:

同样,让我们从实例化一个MultiValuedMap开始。

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

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

6.将多个键映射到一个值

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

6.1.Guava的

Guava offers its Table that satisfies the above use case:

Guava提供的Table可以满足上述用例。

Table<String, String, String> 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.

正如我们所见,我们可以得到行、列和值的Set视图。

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

Table还为我们提供了查询其行或列的能力

Let’s consider a movie table to demonstrate this:

让我们考虑用一个电影表来证明这一点。

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

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

这里有一些不言自明的搜索样本,我们可以在我们的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"));
}

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.

然而,Table限制我们只能将两个键映射到一个值。在Guava中,我们还没有办法将两个以上的键映射到一个值上。

6.2. Apache’s MultiKeyMap

6.2.Apache的MultiKeyMap

Coming back to our cityCoordinates example, here’s how we can manipulate it using 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());
}

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.

从上面的代码片段中我们可以看到,为了得到与Guava的Table相同的断言,我们必须对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:

然而,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 vs. Google Guava

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.

按照其工程师的说法Google Guava的诞生是由于需要在库中使用Apache Commons没有提供的泛型。它也完全遵循集合API的要求。另一个主要优势是,它正处于积极的开发阶段,新的版本会经常出现。

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.

然而,Apache在从集合中取值的性能方面具有优势。不过在插入时间方面,Guava仍然占据优势。

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.

虽然我们在代码样本中只比较了集合API,但Apache Commons作为一个整体,与Guava相比,提供了更多的功能

8. Conclusion

8.结论

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

在本教程中,我们比较了Apache Commons和Google Guava提供的一些功能,特别是在集合框架方面。

Here, we merely scratched the surface of what the two libraries have to offer.

在这里,我们只是触及了这两个图书馆所能提供的东西的表面。

Moreover, it’s not an either-or comparison. As our code samples demonstrated, there’re features unique to each of the two, and there can be situations where both can coexist.

此外,这并不是一个非此即彼的比较。正如我们的代码样本所展示的那样,两者都有各自的特点,而且可能存在两者共存的情况

As always, the source code is available over on GitHub.

一如既往,源代码可在GitHub上获得