Apache Commons Collections Bag

1. Introduction

In this quick article, we’ll focus on how to use the Apache’s Bag collection.

2. Maven Dependency

Before we start, we need to import the latest dependencies from Maven Central:

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

3. Bags vs Collections

Simply put, Bag is a collection that allows storing multiple items along with their repetition count:

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. Violations of the Collection Contract

While reading Bag‘s API documentation, we may notice that some methods are marked as violating the standard Java’s Collection contract.

For example, when we use an add() API from a Java collection, we receive true even if the item is already in the collection:

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

The same API from a Bag implementation will return a false when we add an element which is already available in the collection:

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

To resolve these issues, Apache Collections’ library provides a decorator called the CollectionBag. We can use this to make our bag collections compliant with the Java Collection contract:

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

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

4. Bag Implementations

Let’s now explore the various implementations of the Bag interface – within Apache’s collections library.

4.1. HashBag

We can add an element and instruct the API on the number of copies this element should have in our bag collection:

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

We can also delete a specific number of copies or every instance of an element from our bag:

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

The TreeBag implementation works like any other tree, additionally maintaining Bag semantics.

We can naturally sort an array of integers with a TreeBag and then query the number of instances each individual element has within the collection:

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

The TreeBag implements a SortedBag interface, all implementations of this interface can use the decorator CollectionSortedBag to comply with the Java Collections contract:

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

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

4.3. SynchronizedSortedBag

Another widely used implementation of Bag is the SynchronizedSortedBag. Precisely, this is a synchronized decorator of a SortedBag implementation.

We can use this decorator with our TreeBag (an implementation of SortedBag) from the previous section to synchronize access to our bag:

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

We can use a combination of APIs – Collections.synchronizedSortedMap() and TreeMap – to simulate what we did here with SynchronizedSortedBag.

5. Conclusion

In this short tutorial, we’ve learned about the Bag interface and its various implementations.

As always, the code for this article can be found over on GitHub.

Related posts:

Database Migrations with Flyway
Prevent Brute Force Authentication Attempts with Spring Security
Check If a String Is Numeric in Java
OAuth 2.0 Resource Server With Spring Security 5
Java Program to Implement Hash Tables with Double Hashing
Hướng dẫn sử dụng Lớp FilePermission trong java
Stack Memory and Heap Space in Java
LinkedHashSet trong Java hoạt động như thế nào?
Java Program to Implement Double Order Traversal of a Binary Tree
Java Program to Implement Gift Wrapping Algorithm in Two Dimensions
Java Program to Find the Vertex Connectivity of a Graph
Spring RestTemplate Error Handling
Why String is Immutable in Java?
ETL with Spring Cloud Data Flow
Spring Boot - Build Systems
Java Program to Implement Flood Fill Algorithm
Programmatic Transaction Management in Spring
Guide to the Volatile Keyword in Java
Checked and Unchecked Exceptions in Java
Guide to @JsonFormat in Jackson
Java Program to Implement HashMap API
Tránh lỗi NullPointerException trong Java như thế nào?
Java Program to Implement Hash Tables with Quadratic Probing
Java Program to Perform Arithmetic Operations on Numbers of Size
Java Program to Implement Heap’s Algorithm for Permutation of N Numbers
Java Program to Find Nearest Neighbor for Dynamic Data Set
Java Program to Implement Dijkstra’s Algorithm using Set
Java Program to Implement Gabow Algorithm
Truyền giá trị và tham chiếu trong java
Vấn đề Nhà sản xuất (Producer) – Người tiêu dùng (Consumer) và đồng bộ hóa các luồng trong Java
Compact Strings in Java 9
Using the Map.Entry Java Class