«1. Введение

MapUtils — это один из инструментов, доступных в проекте Apache Commons Collections.

Проще говоря, он предоставляет служебные методы и декораторы для работы с экземплярами java.util.Map и java.util.SortedMap.

2. Настройка

Начнем с добавления зависимости:

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

3. Утилиты

3.1. Создание карты из массива

Теперь давайте настроим массивы, которые мы будем использовать для создания карты:

public class MapUtilsTest {
    private String[][] color2DArray = new String[][] {
        {"RED", "#FF0000"},
        {"GREEN", "#00FF00"},
        {"BLUE", "#0000FF"}
    };
    private String[] color1DArray = new String[] {
        "RED", "#FF0000",
        "GREEN", "#00FF00",
        "BLUE", "#0000FF"
    };
    private Map<String, String> colorMap;

    //...
}

Давайте посмотрим, как мы можем создать карту из двумерного массива:

@Test
public void whenCreateMapFrom2DArray_theMapIsCreated() {
    this.colorMap = MapUtils.putAll(
      new HashMap<>(), this.color2DArray);

    assertThat(
      this.colorMap, 
      is(aMapWithSize(this.color2DArray.length)));
    
    assertThat(this.colorMap, hasEntry("RED", "#FF0000"));
    assertThat(this.colorMap, hasEntry("GREEN", "#00FF00"));
    assertThat(this.colorMap, hasEntry("BLUE", "#0000FF"));
}

~~ ~ Мы могли бы также использовать одномерный массив. В этом случае массив рассматривается как ключи и значения в альтернативных индексах:

@Test
public void whenCreateMapFrom1DArray_theMapIsCreated() {
    this.colorMap = MapUtils.putAll(
      new HashMap<>(), this.color1DArray);
    
    assertThat(
      this.colorMap, 
      is(aMapWithSize(this.color1DArray.length / 2)));

    assertThat(this.colorMap, hasEntry("RED", "#FF0000"));
    assertThat(this.colorMap, hasEntry("GREEN", "#00FF00"));
    assertThat(this.colorMap, hasEntry("BLUE", "#0000FF"));
}

3.2. Печать содержимого карты

Много раз во время отладки или в журналах отладки мы хотели бы напечатать всю карту:

@Test
public void whenVerbosePrintMap_thenMustPrintFormattedMap() {
    MapUtils.verbosePrint(System.out, "Optional Label", this.colorMap);
}

И результат:

Optional Label = 
{
    RED = #FF0000
    BLUE = #0000FF
    GREEN = #00FF00
}

Мы также можем использовать debugPrint() который дополнительно печатает типы данных значений.

3.3. Получение значений

MapUtils предоставляет несколько методов для извлечения значения из карты для заданного ключа безопасным для нуля способом.

Например, getString() получает строку из карты. Значение String получается с помощью toString(). Мы можем дополнительно указать значение по умолчанию, которое будет возвращено, если значение равно null или если преобразование не удалось:

@Test
public void whenGetKeyNotPresent_thenMustReturnDefaultValue() {
    String defaultColorStr = "COLOR_NOT_FOUND";
    String color = MapUtils
      .getString(this.colorMap, "BLACK", defaultColorStr);
    
    assertEquals(color, defaultColorStr);
}

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

@Test
public void whenGetOnNullMap_thenMustReturnDefaultValue() {
    String defaultColorStr = "COLOR_NOT_FOUND";
    String color = MapUtils.getString(null, "RED", defaultColorStr);
    
    assertEquals(color, defaultColorStr);
}

~~ ~ Здесь цвет получит значение COLOR_NOT_FOUND, даже если карта пуста.

3.4. Инвертирование карты

Мы также можем легко перевернуть карту:

@Test
public void whenInvertMap_thenMustReturnInvertedMap() {
    Map<String, String> invColorMap = MapUtils.invertMap(this.colorMap);

    int size = invColorMap.size();
    Assertions.assertThat(invColorMap)
      .hasSameSizeAs(colorMap)
      .containsKeys(this.colorMap.values().toArray(new String[] {}))
      .containsValues(this.colorMap.keySet().toArray(new String[] {}));
}

Это инвертирует colorMap на:

{
    #00FF00 = GREEN
    #FF0000 = RED
    #0000FF = BLUE
}

Если исходная карта связывает одно и то же значение для нескольких ключей, то после инвертирования одно из значений станет ключом случайным образом.

3.5. Нулевые и пустые проверки Метод

isEmpty() возвращает true, если карта является нулевой или пустой.

Метод safeAddToMap() предотвращает добавление нулевых элементов в карту.

4. Декораторы

Эти методы добавляют карте дополнительные функциональные возможности.

В большинстве случаев рекомендуется не хранить ссылку на декорированную карту.

4.1. Карта фиксированного размера

fixedSizeMap() возвращает карту фиксированного размера, поддерживаемую данной картой. Элементы можно изменять, но нельзя добавлять или удалять:

@Test(expected = IllegalArgumentException.class)
public void whenCreateFixedSizedMapAndAdd_thenMustThrowException() {
    Map<String, String> rgbMap = MapUtils
      .fixedSizeMap(MapUtils.putAll(new HashMap<>(), this.color1DArray));
    
    rgbMap.put("ORANGE", "#FFA500");
}

4.2. Predicated Map

Метод predicatedMap() возвращает Map и гарантирует, что все удерживаемые элементы соответствуют предоставленному предикату:

@Test(expected = IllegalArgumentException.class)
public void whenAddDuplicate_thenThrowException() {
    Map<String, String> uniqValuesMap 
      = MapUtils.predicatedMap(this.colorMap, null, 
        PredicateUtils.uniquePredicate());
    
    uniqValuesMap.put("NEW_RED", "#FF0000");
}

Здесь мы указали предикат для значений с помощью PredicateUtils.uniquePredicate(). Любая попытка вставить повторяющееся значение в эту карту приведет к исключению java.lang.IllegalArgumentException.

Мы можем реализовать пользовательские предикаты, реализовав интерфейс Predicate.

4.3. Lazy Map

lazyMap() возвращает карту, значения которой инициализируются по запросу.

Если ключ, переданный методу Map.get(Object) этой карты, отсутствует на карте, экземпляр Transformer будет использоваться для создания нового объекта, который будет связан с запрошенным ключом:

@Test
public void whenCreateLazyMap_theMapIsCreated() {
    Map<Integer, String> intStrMap = MapUtils.lazyMap(
      new HashMap<>(),
      TransformerUtils.stringValueTransformer());
    
    assertThat(intStrMap, is(anEmptyMap()));
    
    intStrMap.get(1);
    intStrMap.get(2);
    intStrMap.get(3);
    
    assertThat(intStrMap, is(aMapWithSize(3)));
}

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

В этом кратком руководстве мы изучили класс MapUtils Apache Commons Collections и рассмотрели различные служебные методы и декораторы, которые могут упростить различные распространенные операции с картами.

Как обычно, код доступен на GitHub.

Next »

Guide to Apache Commons CircularFifoQueue

« Previous

A Guide to Apache Commons Collections CollectionUtils