1. Введение
В этой быстрой статье мы сосредоточимся на том, как использовать коллекцию Apache's Bag
.
2. Зависимость от Maven
Прежде чем мы начнем, нам нужно импортировать последние зависимости из Maven Central :
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
3. Сумки против коллекций
Проще говоря, 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 Collection.
Например, когда мы используем 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 Collection :
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. ХэшБэг
Мы можем добавить элемент и указать 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
работает как любое другое дерево, дополнительно поддерживая семантику 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. СинхронизированныйSortedBag
Другой широко используемой реализацией 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 .