«1. Введение

В этой быстрой статье мы сосредоточимся на том, как использовать коллекцию Apache’s Bag.

2. Зависимость Maven

Прежде чем мы начнем, нам нужно импортировать последние зависимости из Maven Central:

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

3. Bags vs Collections

Проще говоря, 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. Нарушения контракта на сбор

Читая документацию по API Bag, мы можем заметить, что некоторые методы помечены как нарушающие стандартный контракт Java на сбор.

Например, когда мы используем API add() из коллекции Java, мы получаем true, даже если элемент уже находится в коллекции:

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

Тот же API из реализации Bag вернет false, когда мы добавляем элемент, который уже доступен в коллекции:

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

Чтобы решить эти проблемы, библиотека Apache Collections предоставляет декоратор, который называется CollectionBag. Мы можем использовать это, чтобы сделать наши коллекции пакетов совместимыми с контрактом коллекции Java:

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

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

4. Реализации пакетов

Давайте теперь рассмотрим различные реализации интерфейса Bag — в библиотеке коллекций Apache.

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:

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.

Мы можем использовать этот декоратор с нашим 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.

Next »

Apache Commons Collections SetUtils