Перейти к основному содержимому

Коллекции Apache Commons OrderedMap

· 4 мин. чтения

1. Обзор

Библиотека Apache Commons Collections предоставляет полезные классы, дополняющие Java Collections Framework.

В этой статье мы рассмотрим интерфейс OrderedMap , который расширяет java.util.Map .

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

Первое, что нам нужно сделать, это добавить зависимость Maven в наш pom.xml :

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

Вы можете найти последнюю версию библиотеки в репозитории Maven Central .

3. Свойства OrderedMap

Проще говоря, карта, реализующая интерфейс OrderedMap :

  • Поддерживает порядок в своем наборе ключей, хотя набор не отсортирован
  • Можно повторять в обоих направлениях с помощью методов: firstKey() и nextKey() или lastKey() и previousKey() .
  • Можно пройти с помощью MapIterator (также предоставляется библиотекой)
  • Предоставляет методы для поиска, изменения, удаления или замены элементов.

4. Использование OrderedMap

Давайте настроим OrderedMap бегунов и их возраст в тестовом классе. Мы будем использовать LinkedMap — одну из реализаций OrderedMap, представленных в библиотеке.

Во-первых, давайте настроим массивы бегунов и возрастов, которые мы будем использовать для загрузки карты и проверки порядка значений:

public class OrderMapUnitTest {
private String[] names = {"Emily", "Mathew", "Rose", "John", "Anna"};
private Integer[] ages = {37, 28, 40, 36, 21};
private LinkedMap<String, Integer> runnersLinkedMap;

//...
}

Теперь давайте инициализируем нашу карту:

@Before
public void createRunners() {
this.runnersLinkedMap = new LinkedMap<>();

for (int i = 0; i < RUNNERS_COUNT; i++) {
runners.put(this.names[i], this.ages[i]);
}
}

4.1. Прямая итерация

Давайте посмотрим, как используется прямой итератор:

@Test
public void givenALinkedMap_whenIteratedForwards_thenPreservesOrder() {
String name = this.runnersLinkedMap.firstKey();
int i = 0;
while (name != null) {
assertEquals(name, names[i]);
name = this.runnersLinkedMap.nextKey(name);
i++;
}
}

Обратите внимание, что когда мы достигли последнего ключа, метод nextKey() вернет нулевое значение.

4.2. Обратная итерация

Теперь давайте вернемся назад, начиная с последнего ключа:

@Test
public void givenALinkedMap_whenIteratedBackwards_thenPreservesOrder() {
String name = this.runnersLinkedMap.lastKey();
int i = RUNNERS_COUNT - 1;
while (name != null) {
assertEquals(name, this.names[i]);
name = this.runnersLinkedMap.previousKey(name);
i--;
}
}

Как только мы достигнем первого ключа, метод previousKey() вернет значение null.

4.3. Пример MapIterator

Теперь давайте воспользуемся методом mapIterator() для получения MapIterator , поскольку мы покажем, как он сохраняет порядок бегунов, определенный в именах и возрастах массивов :

@Test
public void givenALinkedMap_whenIteratedWithMapIterator_thenPreservesOrder() {
OrderedMapIterator<String, Integer> runnersIterator
= this.runnersLinkedMap.mapIterator();

int i = 0;
while (runnersIterator.hasNext()) {
runnersIterator.next();

assertEquals(runnersIterator.getKey(), this.names[i]);
assertEquals(runnersIterator.getValue(), this.ages[i]);
i++;
}
}

4.4. Удаление элементов

Наконец, давайте проверим, как можно удалить элемент по индексу или по объекту :

@Test
public void givenALinkedMap_whenElementRemoved_thenSizeDecrease() {
LinkedMap<String, Integer> lmap
= (LinkedMap<String, Integer>) this.runnersLinkedMap;

Integer johnAge = lmap.remove("John");

assertEquals(johnAge, new Integer(36));
assertEquals(lmap.size(), RUNNERS_COUNT - 1);

Integer emilyAge = lmap.remove(0);

assertEquals(emilyAge, new Integer(37));
assertEquals(lmap.size(), RUNNERS_COUNT - 2);
}

5. Предоставленные реализации

На данный момент в версии 4.1 библиотеки есть две реализации интерфейса OrderedMapListOrderedMap и LinkedMap .

ListOrderedMap отслеживает порядок набора ключей с помощью java.util.List . Это декоратор OrderedMap и может быть создан из любой карты с помощью статического метода ListOrderedMap.decorate(Map map) .

LinkedMap основан на HashMap и улучшает его, позволяя двунаправленную итерацию и другие методы интерфейса OrderedMap .

Обе реализации также предоставляют три метода, которые находятся за пределами интерфейса OrderedMap :

  • asList() — получает список типа List<K> (где K — тип ключей) с сохранением порядка карты
  • get(int index) — получает элемент по индексу позиции, в отличие от метода get(Object o) , предоставленного в интерфейсе.
  • indexOf(Object o) – получает индекс объекта o в упорядоченной карте

Мы можем преобразовать OrderedMap в LinkedMap , чтобы использовать метод asList() :

@Test
public void givenALinkedMap_whenConvertedToList_thenMatchesKeySet() {
LinkedMap<String, Integer> lmap
= (LinkedMap<String, Integer>) this.runnersLinkedMap;

List<String> listKeys = new ArrayList<>();
listKeys.addAll(this.runnersLinkedMap.keySet());
List<String> linkedMap = lmap.asList();

assertEquals(listKeys, linkedMap);
}

Затем мы можем проверить работу методов indexOf(Object o) и get(int index) в реализации LinkedMap :

@Test
public void givenALinkedMap_whenSearchByIndexIsUsed_thenMatchesConstantArray() {
LinkedMap<String, Integer> lmap
= (LinkedMap<String, Integer>) this.runnersLinkedMap;

for (int i = 0; i < RUNNERS_COUNT; i++) {
String name = lmap.get(i);

assertEquals(name, this.names[i]);
assertEquals(lmap.indexOf(this.names[i]), i);
}
}

6. Заключение

В этом кратком руководстве мы рассмотрели интерфейс OrderedMap , его основные методы и реализации.

Для получения дополнительной информации см . документ JavaDoc библиотеки Apache Commons Collections .

Как всегда, полный тестовый класс для этой статьи содержит аналогичные тестовые примеры с использованием как LinkedMap, так и ListOrderedMap и может быть загружен из проекта GitHub .