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

Инициализировать HashMap в Java

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

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

1. Обзор

В этом руководстве мы узнаем о различных способах инициализации HashMap в Java.

Мы будем использовать Java 8, а также Java 9.

2. Статический инициализатор для статического HashMap

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

public static Map<String, String> articleMapOne;
static {
articleMapOne = new HashMap<>();
articleMapOne.put("ar01", "Intro to Map");
articleMapOne.put("ar02", "Some article");
}

Преимущество такого вида инициализации в том, что карта изменяема, но она будет работать только для статики. Следовательно, записи могут быть добавлены и удалены по мере необходимости.

Давайте продолжим и протестируем это:

@Test
public void givenStaticMap_whenUpdated_thenCorrect() {

MapInitializer.articleMapOne.put(
"NewArticle1", "Convert array to List");

assertEquals(
MapInitializer.articleMapOne.get("NewArticle1"),
"Convert array to List");
}

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

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

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

3. Использование коллекций Java

Если нам нужно создать одноэлементную неизменяемую карту с одной записью, Collections.singletonMap() становится очень полезным:

public static Map<String, String> createSingletonMap() {
return Collections.singletonMap("username1", "password1");
}

Обратите внимание, что карта здесь неизменяемая, и если мы попытаемся добавить больше записей, она вызовет исключение java.lang.UnsupportedOperationException.

Мы также можем создать неизменяемую пустую карту, используя Collections.emptyMap():

Map<String, String> emptyMap = Collections.emptyMap();

4. Путь Java 8

В этом разделе давайте рассмотрим способы инициализации карты с помощью Java 8 Stream API.

4.1. Использование Collectors.toMap()

Давайте воспользуемся Stream двумерного массива String и соберем их в карту:

Map<String, String> map = Stream.of(new String[][] {
{ "Hello", "World" },
{ "John", "Doe" },
}).collect(Collectors.toMap(data -> data[0], data -> data[1]));

Обратите внимание, что тип данных ключа и значения карты одинаковы.

Чтобы сделать его более общим, давайте возьмем массив объектов и выполним ту же операцию:

Map<String, Integer> map = Stream.of(new Object[][] { 
{ "data1", 1 },
{ "data2", 2 },
}).collect(Collectors.toMap(data -> (String) data[0], data -> (Integer) data[1]));

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

4.2. Использование потока Map.Entry

Здесь мы будем использовать экземпляры Map.Entry. Это еще один подход, когда у нас есть разные типы ключей и значений.

Во-первых, давайте воспользуемся реализацией SimpleEntry интерфейса Entry :

Map<String, Integer> map = Stream.of(
new AbstractMap.SimpleEntry<>("idea", 1),
new AbstractMap.SimpleEntry<>("mobile", 2))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Теперь давайте создадим карту, используя реализацию SimpleImmutableEntry :

Map<String, Integer> map = Stream.of(
new AbstractMap.SimpleImmutableEntry<>("idea", 1),
new AbstractMap.SimpleImmutableEntry<>("mobile", 2))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

4.3. Инициализация неизменяемой карты

В некоторых случаях нам нужно инициализировать неизменяемую карту. Это можно сделать, обернув Collectors.toMap() внутри Collectors.collectingAndThen() :

Map<String, String> map = Stream.of(new String[][] { 
{ "Hello", "World" },
{ "John", "Doe" },
}).collect(Collectors.collectingAndThen(
Collectors.toMap(data -> data[0], data -> data[1]),
Collections::<String, String> unmodifiableMap));

Обратите внимание, что нам следует избегать такой инициализации с помощью Streams, так как это может привести к огромным потерям производительности, а множество объектов-мусоров создается только для инициализации карты.

5. Путь Java 9

Java 9 поставляется с различными фабричными методами в интерфейсе Map , которые упрощают создание и инициализацию неизменяемых карт.

Давайте продолжим и рассмотрим эти фабричные методы.

5.1. Карта()

Этот фабричный метод не принимает аргументов, один аргумент и переменные аргументы:

Map<String, String> emptyMap = Map.of();
Map<String, String> singletonMap = Map.of("key1", "value");
Map<String, String> map = Map.of("key1","value1", "key2", "value2");

Обратите внимание, что этот метод поддерживает максимум 10 пар ключ-значение.

5.2. Карта.ofEntries()

Он похож на Map.of() , но не имеет ограничений на количество пар ключ-значение:

Map<String, String> map = Map.ofEntries(
new AbstractMap.SimpleEntry<String, String>("name", "John"),
new AbstractMap.SimpleEntry<String, String>("city", "budapest"),
new AbstractMap.SimpleEntry<String, String>("zip", "000000"),
new AbstractMap.SimpleEntry<String, String>("home", "1231231231")
);

Обратите внимание, что фабричные методы создают неизменяемые карты, поэтому любая мутация приведет к исключению UnsupportedOperationException.

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

Теперь, если нам нужна изменяемая или растущая карта после инициализации, мы можем создать любую из реализаций интерфейса Map и передать эти неизменяемые карты в конструкторе:

Map<String, String> map = new HashMap<String, String> (
Map.of("key1","value1", "key2", "value2"));
Map<String, String> map2 = new HashMap<String, String> (
Map.ofEntries(
new AbstractMap.SimpleEntry<String, String>("name", "John"),
new AbstractMap.SimpleEntry<String, String>("city", "budapest")));

6. Использование гуавы

Поскольку мы рассмотрели способы использования ядра Java, давайте двигаться дальше и инициализировать карту с помощью библиотеки Guava:

Map<String, String> articles 
= ImmutableMap.of("Title", "My New Article", "Title2", "Second Article");

Это создаст неизменяемую карту и создаст изменяемую:

Map<String, String> articles 
= Maps.newHashMap(ImmutableMap.of("Title", "My New Article", "Title2", "Second Article"));

Метод ImmutableMap.of() также имеет перегруженные версии, которые могут принимать до 5 пар параметров ключ-значение. Вот как будет выглядеть пример с двумя парами параметров:

ImmutableMap.of("key1", "value1", "key2", "value2");

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

В этой статье мы рассмотрели различные способы инициализации карты , в частности, для создания пустых, одноэлементных, неизменяемых и изменяемых карт. Как мы видим, в этой области произошли огромные улучшения по сравнению с Java 9.

Как всегда, исходный код примера находится в проекте Github . Примеры Java 9 находятся здесь , а образец Guava — здесь .