16

Как напрямую инициализировать HashMap (в литеральном виде)?

12

Проблема с инициализацией HashMap в Java

Я пытаюсь инициализировать Java HashMap с заранее известными значениями, но не могу найти подходящий способ сделать это. Вот что я пытаюсь реализовать:

Map<String, String> test = 
    new HashMap<String, String>{"test":"test","test":"test"};

Какой будет правильный синтаксис для создания такой карты? Я не нашел никакой информации по этому поводу. Возможно ли это? Я ищу самый короткий и быстрый способ поместить некоторые "финальные/статические" значения в карту, которые никогда не изменятся и известны заранее при создании карты.

5 ответ(ов)

0

Вы можете инициализировать HashMap в Java с помощью блока инициализации, как показано в вашем примере. Вот перевод кода:

Map<String, String> test = new HashMap<String, String>() {
    {
        put(key1, value1);
        put(key2, value2);
    }
};

В этом коде создаётся анонимный подкласс HashMap, и в блоке инициализации добавляются элементы с помощью метода put(). Это удобный способ инициализации карты, однако следует помнить, что такой подход может привести к созданию ненужного подкласса, что иногда может быть нежелательным с точки зрения производительности и читаемости кода. Если вам нужно просто создать и инициализировать карту, рассмотрите возможность использования метода Map.of() (доступного начиная с Java 9) или других более современных подходов.

0

В качестве альтернативного решения, используя обычные классы Java 7 и varargs, можно создать класс HashMapBuilder с таким методом:

public static HashMap<String, String> build(String... data) {
    HashMap<String, String> result = new HashMap<String, String>();

    if (data.length % 2 != 0) 
        throw new IllegalArgumentException("Нечетное количество аргументов");

    String key = null;
    Integer step = -1;

    for (String value : data) {
        step++;
        switch (step % 2) {
        case 0:
            if (value == null)
                throw new IllegalArgumentException("Значение ключа не может быть null");
            key = value;
            continue;
        case 1:
            result.put(key, value);
            break;
        }
    }

    return result;
}

Используйте этот метод следующим образом:

HashMap<String, String> data = HashMapBuilder.build("key1", "value1", "key2", "value2");

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

0

В чистом Java 8 у вас также есть возможность использовать Streams/Collectors, чтобы выполнить эту задачу.

Map<String, String> myMap = Stream.of(
         new SimpleEntry<>("key1", "value1"),
         new SimpleEntry<>("key2", "value2"),
         new SimpleEntry<>("key3", "value3"))
        .collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue));

Это имеет преимущество в том, что не создается анонимный класс.

Обратите внимание на необходимые импорты:

import static java.util.stream.Collectors.toMap;
import java.util.AbstractMap.SimpleEntry;

Конечно, как упоминалось в других ответах, начиная с Java 9, есть более простые способы сделать то же самое.

0

Хочу дать небольшое предупреждение относительно ответа Johnny Willer'a.

Collectors.toMap основан на методе Map.merge и не ожидает null-значений, поэтому при попытке использовать null в качестве значения будет выброшено исключение NullPointerException, как описано в этом отчете о баге: https://bugs.openjdk.java.net/browse/JDK-8148463

Кроме того, если ключ встречается несколько раз, то стандартный Collectors.toMap выбросит IllegalStateException.

Альтернативным способом получить карту с null-значениями, используя синтаксис билдера в Java 8, является написание пользовательского селектора с использованием HashMap (так как он позволяет null-значения):

Map<String, String> myMap = Stream.of(
         new SimpleEntry<>("key1", "value1"),
         new SimpleEntry<>("key2", (String) null),
         new SimpleEntry<>("key3", "value3"),
         new SimpleEntry<>("key1", "value1updated"))
        .collect(HashMap::new,
                (map, entry) -> map.put(entry.getKey(),
                                        entry.getValue()),
                HashMap::putAll);

Таким образом, вы сможете безопасно работать с null-значениями при создании карты.

0

Мы используем простой утилитный класс для инициализации карт в "плавном" стиле:

Map<String, String> map = MapInit
    .init("key1", "value1")
    .put("key2", "value2")
    .put("key3", "value3")
    .getMap();

Этот утилитный класс не ограничен ни по типу ключей и значений, ни по количеству элементов, ни по типу создаваемой карты.

Вот как выглядит этот утилитный класс:

public class MapInit<K, V, T extends Map<K, V>> {
    private final T map;

    private MapInit(final T map) {
        this.map = map;
    }

    public T getMap() {
        return this.map;
    }

    public static <K, V> MapInit<K, V, HashMap<K, V>> init(final K key, final V value) {
        return init(HashMap::new, key, value);
    }

    public static <K, V, T extends Map<K, V>> MapInit<K, V, T> init(final Supplier<T> mapSupplier, final K key, final V value) {
        return new MapInit<>(mapSupplier.get()) //
                .put(key, value);
    }

    public MapInit<K, V, T> put(final K key, final V value) {
        this.map.put(key, value);
        return this;
    }
}

Таким образом, вы можете легко создавать и инициализировать карты, добавляя элементы в "плавном" стиле, что делает код более читабельным и удобным для использования.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь