Как напрямую инициализировать HashMap (в литеральном виде)?
Проблема с инициализацией HashMap в Java
Я пытаюсь инициализировать Java HashMap с заранее известными значениями, но не могу найти подходящий способ сделать это. Вот что я пытаюсь реализовать:
Map<String, String> test =
new HashMap<String, String>{"test":"test","test":"test"};
Какой будет правильный синтаксис для создания такой карты? Я не нашел никакой информации по этому поводу. Возможно ли это? Я ищу самый короткий и быстрый способ поместить некоторые "финальные/статические" значения в карту, которые никогда не изменятся и известны заранее при создании карты.
5 ответ(ов)
Вы можете инициализировать HashMap
в Java с помощью блока инициализации, как показано в вашем примере. Вот перевод кода:
Map<String, String> test = new HashMap<String, String>() {
{
put(key1, value1);
put(key2, value2);
}
};
В этом коде создаётся анонимный подкласс HashMap
, и в блоке инициализации добавляются элементы с помощью метода put()
. Это удобный способ инициализации карты, однако следует помнить, что такой подход может привести к созданию ненужного подкласса, что иногда может быть нежелательным с точки зрения производительности и читаемости кода. Если вам нужно просто создать и инициализировать карту, рассмотрите возможность использования метода Map.of()
(доступного начиная с Java 9) или других более современных подходов.
В качестве альтернативного решения, используя обычные классы 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
.
В чистом 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, есть более простые способы сделать то же самое.
Хочу дать небольшое предупреждение относительно ответа 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-значениями при создании карты.
Мы используем простой утилитный класс для инициализации карт в "плавном" стиле:
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;
}
}
Таким образом, вы можете легко создавать и инициализировать карты, добавляя элементы в "плавном" стиле, что делает код более читабельным и удобным для использования.
Как инициализировать статическую Map?
Эффективный способ итерации по каждой записи в Java Map?
Инициализация ArrayList в одну строчку
Сортировка Map<Key, Value> по значениям
Как инициализировать значения HashSet при создании?