«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.
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 библиотеки есть две реализации OrderedMap интерфейс — ListOrderedMap и LinkedMap.
ListOrderedMap отслеживает порядок набора ключей с помощью java.util.List. Это декоратор OrderedMap, который можно создать из любой карты с помощью статического метода ListOrderedMap.decorate(Map map).
LinkedMap основан на HashMap и улучшен, позволяя двунаправленную итерацию и другие методы интерфейса OrderedMap.
Обе реализации также предоставляют три метода, которые находятся вне интерфейса OrderedMap:
-
asList() — получает список типа List\u003cK\u003e (где 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.