Contents

Apache Commons Bag简介

1.简介

在这篇快速文章中,我们将重点介绍如何使用 Apache 的Bag集合。

2. Maven依赖

在开始之前,我们需要从Maven Central 导入最新的依赖项:

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

3. Bag与Collection

简而言之,Bag是一个允许存储多个项目及其重复次数的集合:

public void whenAdded_thenCountIsKept() {
    Bag<Integer> bag = new HashBag<>(
      Arrays.asList(1, 2, 3, 3, 3, 1, 4));
        
    assertThat(2, equalTo(bag.getCount(1)));
}

3.1. 违反Collection

在阅读Bag的 API 文档时,我们可能会注意到某些方法被标记为违反标准 Java 的 Collection。

例如,当我们使用 Java 集合中的add() API 时,即使项目已经在集合中,我们也会收到true

Collection<Integer> collection = new ArrayList<>();
collection.add(1);
assertThat(collection.add(1), is(true));

当我们添加集合中已经可用的元素时,来自Bag实现的相同 API将返回false

Bag<Integer> bag = new HashBag<>();
bag.add(1);
 
assertThat(bag.add(1), is(not(true)));

为了解决这些问题,Apache Collections 的库提供了一个名为CollectionBag 的装饰器。我们可以使用它来使我们的包集合符合 Java Collection合约:

public void whenBagAddAPILikeCollectionAPI_thenTrue() {
    Bag<Integer> bag = CollectionBag.collectionBag(new HashBag<>());
    bag.add(1);
    assertThat(bag.add(1), is((true)));
}

4. Bag的实现

现在让我们在 Apache 的集合库中探索Bag接口的各种实现。

4.1. HashBag

我们可以添加一个元素并指示 API 该元素在我们的包集合中应具有的副本数:

public void givenAdd_whenCountOfElementsDefined_thenCountAreAdded() {
    Bag<Integer> bag = new HashBag<>();
	
    bag.add(1, 5); // adding 1 five times
 
    assertThat(5, equalTo(bag.getCount(1)));
}

我们还可以从我们的包中删除特定数量的副本或元素的每个实例:

public void givenMultipleCopies_whenRemove_allAreRemoved() {
    Bag<Integer> bag = new HashBag<>(
      Arrays.asList(1, 2, 3, 3, 3, 1, 4));
    bag.remove(3, 1); // remove one element, two still remain
    assertThat(2, equalTo(bag.getCount(3)));
	
    bag.remove(1); // remove all
    assertThat(0, equalTo(bag.getCount(1)));
}

4.2. TreeBag

TreeBag实现与任何其他树一样工作,另外还维护Bag语义。

我们可以自然地使用TreeBag对整数数组进行排序,然后查询每个单独元素在集合中的实例数:

public void givenTree_whenDuplicateElementsAdded_thenSort() {
    TreeBag<Integer> bag = new TreeBag<>(Arrays.asList(7, 5,
      1, 7, 2, 3, 3, 3, 1, 4, 7));
    
    assertThat(bag.first(), equalTo(1));
    assertThat(bag.getCount(bag.first()), equalTo(2));
    assertThat(bag.last(), equalTo(7));
    assertThat(bag.getCount(bag.last()), equalTo(3));
}

TreeBag实现了一个SortedBag接口,该接口的所有实现都可以使用装饰器CollectionSortedBag来遵守 Java Collections 契约:

public void whenTreeAddAPILikeCollectionAPI_thenTrue() {
    SortedBag<Integer> bag 
      = CollectionSortedBag.collectionSortedBag(new TreeBag<>());
    bag.add(1);
 
    assertThat(bag.add(1), is((true)));
}

4.3. SynchronizedSortedBag

Bag的另一个广泛使用的实现是SynchronizedSortedBag。准确地说,这是一个SortedBag实现的同步装饰器。

我们可以将此装饰器与上一节中的TreeBagSortedBag 的实现)一起使用,以同步对我们包的访问:

public void givenSortedBag_whenDuplicateElementsAdded_thenSort() {
    SynchronizedSortedBag<Integer> bag = SynchronizedSortedBag
      .synchronizedSortedBag(new TreeBag<>(
        Arrays.asList(7, 5, 1, 7, 2, 3, 3, 3, 1, 4, 7)));
    
    assertThat(bag.first(), equalTo(1));
    assertThat(bag.getCount(bag.first()), equalTo(2));
    assertThat(bag.last(), equalTo(7));
    assertThat(bag.getCount(bag.last()), equalTo(3));
}

我们可以使用 API 的组合——Collections.synchronizedSortedMap()TreeMap——来模拟我们在这里使用SynchronizedSortedBag所做的事情。