CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apache-commons--commons-collections4

The Apache Commons Collections package contains types that extend and augment the Java Collections Framework

Pending
Overview
Eval results
Files

bags.mddocs/

Bags and Counting Collections

Bags (also known as MultiSets) are collections that count the number of times an object appears in the collection. Unlike regular sets which only track presence/absence, bags maintain occurrence counts for each unique element.

Core Interfaces

Bag<E> Interface

The primary interface for bag collections that count element occurrences.

import org.apache.commons.collections4.Bag;
import org.apache.commons.collections4.bag.HashBag;
import java.util.Set;

Bag<String> bag = new HashBag<>();

// Add elements (returns true if collection changed)
boolean added = bag.add("apple");           // Count: apple=1
bag.add("apple", 3);                        // Count: apple=4
bag.add("banana", 2);                       // Count: banana=2

// Query counts
int appleCount = bag.getCount("apple");     // Returns 4
int bananaCount = bag.getCount("banana");   // Returns 2  
int orangeCount = bag.getCount("orange");   // Returns 0 (not present)

// Collection properties
int totalSize = bag.size();                 // Returns 6 (4+2)
Set<String> unique = bag.uniqueSet();       // Returns {"apple", "banana"}
int uniqueCount = unique.size();            // Returns 2

// Remove elements
boolean removed = bag.remove("apple");      // Removes 1, count now 3
bag.remove("apple", 2);                     // Removes 2, count now 1
bag.remove("banana", 5);                    // Removes all (only 2 existed)

// Check presence
boolean hasApple = bag.contains("apple");   // true (count > 0)
boolean hasBanana = bag.contains("banana"); // false (count = 0)

SortedBag<E> Interface

A bag that maintains elements in sorted order by their natural ordering or a provided comparator.

import org.apache.commons.collections4.SortedBag;
import org.apache.commons.collections4.bag.TreeBag;
import java.util.Comparator;

// Natural ordering
SortedBag<String> sortedBag = new TreeBag<>();
sortedBag.add("zebra", 2);
sortedBag.add("apple", 3);  
sortedBag.add("banana", 1);

// Elements maintain sorted order
String first = sortedBag.first();          // Returns "apple"
String last = sortedBag.last();            // Returns "zebra" 

// Custom comparator (reverse order)
SortedBag<String> reverseBag = new TreeBag<>(Comparator.reverseOrder());
reverseBag.addAll(sortedBag);
String firstReverse = reverseBag.first();   // Returns "zebra"
String lastReverse = reverseBag.last();     // Returns "apple"

MultiSet<E> Interface

An alternative interface to Bag with slightly different semantics and additional methods.

import org.apache.commons.collections4.MultiSet;
import org.apache.commons.collections4.multiset.HashMultiSet;
import java.util.Set;

MultiSet<String> multiset = new HashMultiSet<>();

// Add and set counts
multiset.add("item", 5);                    // Add 5 occurrences
int oldCount = multiset.setCount("item", 3); // Set to 3, returns old count (5)

// Entry iteration with counts
Set<MultiSet.Entry<String>> entries = multiset.entrySet();
for (MultiSet.Entry<String> entry : entries) {
    String element = entry.getElement();
    int count = entry.getCount();
    System.out.println(element + ": " + count);
}

// Unique elements
Set<String> uniqueElements = multiset.uniqueSet();

Concrete Implementations

HashBag<E>

A hash-based bag implementation offering O(1) average case performance for basic operations.

import org.apache.commons.collections4.bag.HashBag;
import java.util.Collection;
import java.util.Arrays;

// Create empty bag
HashBag<Integer> numbers = new HashBag<>();

// Create from existing collection  
Collection<String> words = Arrays.asList("the", "quick", "brown", "the", "fox");
HashBag<String> wordCounts = new HashBag<>(words);
// Result: {the=2, quick=1, brown=1, fox=1}

// Bulk operations
numbers.addAll(Arrays.asList(1, 2, 2, 3, 3, 3));
System.out.println(numbers.getCount(1)); // 1
System.out.println(numbers.getCount(2)); // 2 
System.out.println(numbers.getCount(3)); // 3

TreeBag<E>

A tree-based sorted bag implementation with O(log n) performance and element ordering.

import org.apache.commons.collections4.bag.TreeBag;
import java.util.Comparator;

// Natural ordering
TreeBag<String> words = new TreeBag<>();
words.add("zebra", 1);
words.add("apple", 3);
words.add("banana", 2);

// Iterate in sorted order
for (String word : words.uniqueSet()) {
    System.out.println(word + ": " + words.getCount(word));
}
// Output: apple: 3, banana: 2, zebra: 1

// Custom comparator (by string length)
TreeBag<String> byLength = new TreeBag<>(Comparator.comparing(String::length));
byLength.add("a", 1);
byLength.add("hello", 2);  
byLength.add("hi", 3);

String shortest = byLength.first(); // "a" (length 1)
String longest = byLength.last();   // "hello" (length 5)

HashMultiSet<E>

Hash-based implementation of the MultiSet interface.

import org.apache.commons.collections4.multiset.HashMultiSet;

HashMultiSet<Character> charCounts = new HashMultiSet<>();

// Count character frequencies
String text = "hello world";
for (char c : text.toCharArray()) {
    if (c != ' ') {
        charCounts.add(c);
    }
}

// Get most frequent character
char mostFrequent = charCounts.entrySet().stream()
    .max(Comparator.comparing(MultiSet.Entry::getCount))
    .map(MultiSet.Entry::getElement)
    .orElse('\0');
System.out.println("Most frequent: " + mostFrequent); // 'l' (count: 3)

CollectionBag<E>

A bag implementation that wraps an existing collection and maintains element counts.

import org.apache.commons.collections4.bag.CollectionBag;
import java.util.ArrayList;
import java.util.List;

// Wrap an existing collection
List<String> existingList = new ArrayList<>();
existingList.add("apple");
existingList.add("banana");
existingList.add("apple");

CollectionBag<String> collectionBag = new CollectionBag<>(existingList);

// Bag operations work on the underlying collection
int appleCount = collectionBag.getCount("apple"); // 2
collectionBag.add("cherry", 3);

// Changes are reflected in original collection
System.out.println(existingList.size()); // 6 (apple, banana, apple, cherry, cherry, cherry)

CollectionSortedBag<E>

A sorted bag implementation that wraps an existing sorted collection.

import org.apache.commons.collections4.bag.CollectionSortedBag;
import java.util.TreeSet;
import java.util.Set;

// Wrap an existing sorted collection
Set<String> sortedSet = new TreeSet<>();
sortedSet.add("zebra");
sortedSet.add("apple");
sortedSet.add("banana");

CollectionSortedBag<String> sortedCollectionBag = new CollectionSortedBag<>(sortedSet);

// Maintains sorted order
String first = sortedCollectionBag.first();  // "apple"
String last = sortedCollectionBag.last();    // "zebra"

UnmodifiableBag<E> and UnmodifiableSortedBag<E>

Immutable views of bags that prevent modification.

import org.apache.commons.collections4.bag.UnmodifiableBag;
import org.apache.commons.collections4.bag.UnmodifiableSortedBag;
import org.apache.commons.collections4.BagUtils;

Bag<String> mutableBag = new HashBag<>();
mutableBag.add("apple", 3);
mutableBag.add("banana", 2);

// Create unmodifiable view
Bag<String> immutableBag = UnmodifiableBag.unmodifiableBag(mutableBag);
// Or use utility method
Bag<String> immutableBag2 = BagUtils.unmodifiableBag(mutableBag);

// Reading operations work
int count = immutableBag.getCount("apple"); // 3

// Modification attempts throw UnsupportedOperationException
// immutableBag.add("cherry"); // Throws exception

// For sorted bags
SortedBag<String> mutableSorted = new TreeBag<>();
mutableSorted.add("zebra", 2);
mutableSorted.add("apple", 1);

SortedBag<String> immutableSorted = UnmodifiableSortedBag.unmodifiableSortedBag(mutableSorted);
String first = immutableSorted.first(); // "apple" (reading works)
// immutableSorted.add("banana"); // Throws exception

Bag Decorators

Thread-Safe Bags

import org.apache.commons.collections4.BagUtils;
import org.apache.commons.collections4.bag.HashBag;
import org.apache.commons.collections4.bag.SynchronizedBag;

// Create thread-safe bag
Bag<String> safeBag = BagUtils.synchronizedBag(new HashBag<>());

// Or create directly
Bag<String> directSafe = SynchronizedBag.synchronizedBag(new HashBag<>());

// Thread-safe operations
safeBag.add("item", 5);  // Safe for concurrent access
int count = safeBag.getCount("item"); // Safe for concurrent access

// Note: Iteration requires external synchronization
synchronized(safeBag) {
    for (String item : safeBag.uniqueSet()) {
        System.out.println(item + ": " + safeBag.getCount(item));
    }
}

Unmodifiable Bags

import org.apache.commons.collections4.BagUtils;
import org.apache.commons.collections4.bag.HashBag;

Bag<String> mutableBag = new HashBag<>();
mutableBag.add("read-only", 3);

// Create unmodifiable view
Bag<String> readOnlyBag = BagUtils.unmodifiableBag(mutableBag);

// Reading works fine
int count = readOnlyBag.getCount("read-only"); // Returns 3
boolean contains = readOnlyBag.contains("read-only"); // true

// Modifications throw UnsupportedOperationException
try {
    readOnlyBag.add("new-item");
} catch (UnsupportedOperationException e) {
    System.out.println("Cannot modify unmodifiable bag");
}

Predicated Bags

Bags that validate elements before adding them.

import org.apache.commons.collections4.BagUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.collections4.bag.HashBag;

// Only allow positive integers
Predicate<Integer> positiveOnly = n -> n > 0;
Bag<Integer> positiveBag = BagUtils.predicatedBag(new HashBag<>(), positiveOnly);

positiveBag.add(5, 2);    // Succeeds, count: 5=2
positiveBag.add(10);      // Succeeds, count: 10=1

try {
    positiveBag.add(-1);  // Throws IllegalArgumentException
} catch (IllegalArgumentException e) {
    System.out.println("Negative numbers not allowed");
}

// String length validation
Predicate<String> maxLength = s -> s.length() <= 10;
Bag<String> shortStrings = BagUtils.predicatedBag(new HashBag<>(), maxLength);

Transformed Bags

Bags that transform elements before storing them.

import org.apache.commons.collections4.BagUtils;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.bag.HashBag;

// Transform to uppercase
Transformer<String, String> upperCase = String::toUpperCase;
Bag<String> upperBag = BagUtils.transformedBag(new HashBag<>(), upperCase);

upperBag.add("hello");    // Stored as "HELLO"
upperBag.add("world");    // Stored as "WORLD" 
upperBag.add("Hello");    // Adds to existing "HELLO" count

int helloCount = upperBag.getCount("HELLO");  // Returns 2
int worldCount = upperBag.getCount("WORLD");  // Returns 1

// Numeric transformation
Transformer<Integer, Integer> absolute = Math::abs;
Bag<Integer> absBag = BagUtils.transformedBag(new HashBag<>(), absolute);
absBag.add(-5);           // Stored as 5
absBag.add(5);            // Adds to existing 5 count
int fiveCount = absBag.getCount(5); // Returns 2

Utility Operations

BagUtils Class

Comprehensive utility methods for bag operations.

import org.apache.commons.collections4.BagUtils;
import org.apache.commons.collections4.bag.HashBag;

// Empty immutable bag
Bag<String> empty = BagUtils.emptyBag();
System.out.println(empty.isEmpty()); // true

// Create collection-backed bag
Bag<String> baseBag = new HashBag<>();
baseBag.add("item1", 2);
Bag<String> collectionBag = BagUtils.collectionBag(baseBag);

// Synchronization
Bag<Integer> concurrent = BagUtils.synchronizedBag(new HashBag<>());

// Sorted bag synchronization  
SortedBag<String> sortedConcurrent = BagUtils.synchronizedSortedBag(new TreeBag<>());

// Predicate validation
Predicate<String> nonEmpty = s -> !s.isEmpty();
Bag<String> validatedBag = BagUtils.predicatedBag(new HashBag<>(), nonEmpty);

// Transformation
Transformer<String, String> trim = String::trim;
Bag<String> trimmedBag = BagUtils.transformingBag(new HashBag<>(), trim);

Common Use Cases

Frequency Analysis

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

// Count word frequencies in text
List<String> words = Arrays.asList("the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog", "the");

Bag<String> frequencies = new HashBag<>(words);

// Find most common word
String mostCommon = frequencies.uniqueSet().stream()
    .max(Comparator.comparing(frequencies::getCount))
    .orElse(null);
System.out.println("Most common: " + mostCommon); // "the" (appears 3 times)

// Get frequency distribution
Map<String, Integer> distribution = frequencies.uniqueSet().stream()
    .collect(Collectors.toMap(
        word -> word,
        frequencies::getCount
    ));

Statistical Operations

import java.util.DoubleSummaryStatistics;

Bag<Integer> scores = new HashBag<>();
scores.add(85, 3);  // 3 students scored 85
scores.add(90, 2);  // 2 students scored 90  
scores.add(75, 1);  // 1 student scored 75
scores.add(95, 1);  // 1 student scored 95

// Calculate statistics considering frequencies
DoubleSummaryStatistics stats = scores.uniqueSet().stream()
    .flatMapToInt(score -> IntStream.generate(() -> score).limit(scores.getCount(score)))
    .summaryStatistics();

double average = stats.getAverage();    // 85.71
int totalStudents = (int)stats.getCount(); // 7
int maxScore = (int)stats.getMax();     // 95
int minScore = (int)stats.getMin();     // 75

Inventory Management

public class Inventory {
    private final Bag<String> stock = new HashBag<>();
    
    public void addStock(String product, int quantity) {
        stock.add(product, quantity);
    }
    
    public boolean sellProduct(String product, int quantity) {
        if (stock.getCount(product) >= quantity) {
            stock.remove(product, quantity);
            return true;
        }
        return false;
    }
    
    public int getStockLevel(String product) {
        return stock.getCount(product);
    }
    
    public Set<String> getProductsInStock() {
        return stock.uniqueSet();
    }
    
    public int getTotalItems() {
        return stock.size();
    }
    
    public boolean isInStock(String product) {
        return stock.contains(product);
    }
}

Performance Considerations

Choosing the Right Implementation

  • HashBag: Use for fast lookups and when element ordering is not important

    • Add/Remove/Contains: O(1) average case
    • Memory efficient for sparse data
  • TreeBag: Use when you need sorted iteration or range operations

    • Add/Remove/Contains: O(log n)
    • Maintains elements in sorted order
  • HashMultiSet: Use when you need MultiSet interface semantics

    • Similar performance to HashBag
    • Different API for entry iteration

Memory Usage

// Memory-efficient for high counts
Bag<String> efficient = new HashBag<>();
efficient.add("repeated-item", 1_000_000); // Stores count, not 1M objects

// Less efficient with standard collections  
List<String> inefficient = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
    inefficient.add("repeated-item"); // Stores 1M string references
}

Thread Safety Performance

// For high-concurrency scenarios, consider external synchronization
ConcurrentHashMap<String, AtomicInteger> concurrentCounts = new ConcurrentHashMap<>();

// Atomic increment
concurrentCounts.computeIfAbsent("key", k -> new AtomicInteger(0)).incrementAndGet();

// Or use synchronized bag with careful synchronization
Bag<String> syncBag = BagUtils.synchronizedBag(new HashBag<>());

Bags and MultiSets provide powerful abstractions for counting and frequency analysis scenarios. Choose the appropriate implementation based on your performance requirements, thread safety needs, and whether you need ordered iteration.

Install with Tessl CLI

npx tessl i tessl/maven-org-apache-commons--commons-collections4

docs

advanced-collections.md

bags.md

bidimaps.md

collection-utilities.md

functional-programming.md

index.md

multimaps.md

tile.json