1. 序章

このクイック記事では、ApacheのBagコレクションの使用方法に焦点を当てます。

2. Mavenの依存関係

開始する前に、 MavenCentralから最新の依存関係をインポートする必要があります。

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

3. バッグvsコレクション

簡単に言えば、 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. コレクション契約の違反

Bag のAPIドキュメントを読んでいると、一部のメソッドが標準のJavaのコレクションコントラクトに違反しているとマークされていることに気付く場合があります。

たとえば、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)));

これらの問題を解決するために、ApacheCollectionsのライブラリはCollectionBagと呼ばれるデコレータを提供します。これを使用して、バッグコレクションをJava Collectionコントラクトに準拠させることができます。

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

    assertThat(bag.add(1), is((true)));
}

4. バッグの実装

次に、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));
}

TreeBagSortedBagインターフェースを実装します。このインターフェースのすべての実装は、デコレータCollectionSortedBagを使用してJavaコレクションコントラクトに準拠できます。

public void whenTreeAddAPILikeCollectionAPI_thenTrue() {
    SortedBag<Integer> bag 
      = CollectionSortedBag.collectionSortedBag(new TreeBag<>());

    bag.add(1);
 
    assertThat(bag.add(1), is((true)));
}

4.3. SynchronizedSortedBag

Bag のもう1つの広く使用されている実装は、SynchronizedSortedBagです。 正確には、これはSortedBag実装の同期デコレータです。

このデコレータを前のセクションのTreeBag SortedBag の実装)で使用して、バッグへのアクセスを同期できます。

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で行ったことをシミュレートできます。

5. 結論

この短いチュートリアルでは、Bagインターフェースとそのさまざまな実装について学びました。

いつものように、この記事のコードはGitHubにあります。