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

Примеры вложенных HashMaps в Java

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

1. Обзор

В этом руководстве мы рассмотрим, как работать с вложенными HashMaps в Java. Мы также увидим, как создавать и сравнивать их. Наконец, мы также увидим, как удалять и добавлять записи на внутренние карты.

2. Варианты использования

Вложенный HashMap очень полезен для хранения JSON или JSON-подобных структур, в которых объекты встроены друг в друга. Например, структура или JSON, похожие на:

{
"type": "donut",
"batters":
{
“batter”:
[
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" },
{ "id": "1003", "type": "Blueberry" },
{ "id": "1004", "type": "Devil's Food" }
]
}
}

является идеальным кандидатом для вложенного HashMap . В общем, всякий раз, когда нам нужно встроить один объект в другой объект, мы можем их использовать.

3. Создайте хэш -карту

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

3.1. Использование метода put ()

Мы можем создать вложенную HashMap , вручную создав внутренние карты, а затем вставив их во внешнюю карту с помощью метода put:

public Map<Integer, String> buildInnerMap(List<String> batterList) {
Map<Integer, String> innerBatterMap = new HashMap<Integer, String>();
int index = 1;
for (String item : batterList) {
innerBatterMap.put(index, item);
index++;
}
return innerBatterMap;
}

Мы можем проверить это с помощью:

assertThat(mUtil.buildInnerMap(batterList), is(notNullValue()));
Assert.assertEquals(actualBakedGoodsMap.keySet().size(), 2);
Assert.assertThat(actualBakedGoodsMap, IsMapContaining.hasValue(equalTo(mUtil.buildInnerMap(batterList))));

3.2. Использование потоков

Если у нас есть список , который мы хотим преобразовать в карту , мы можем создать поток, а затем преобразовать его в карту с помощью метода Collectors.toMap . Здесь у нас есть два примера: один имеет внутреннюю карту строк , а другой — карту со значениями Integer и Object .

В первом примере в объект Employee вложен объект Address . Затем мы создаем вложенный HashMap :

Map<Integer, Map<String, String>> employeeAddressMap = listEmployee.stream()
.collect(Collectors.groupingBy(e -> e.getAddress().getAddressId(),
Collectors.toMap(f -> f.getAddress().getAddressLocation(), Employee::getEmployeeName)));
return employeeAddressMap;

Во втором примере мы создаем объект типа <Employee id <Address id, Address object>> :

Map<Integer, Map<Integer, Address>> employeeMap = new HashMap<>();
employeeMap = listEmployee.stream().collect(Collectors.groupingBy((Employee emp) -> emp.getEmployeeId(),
Collectors.toMap((Employee emp) -> emp.getAddress().getAddressId(), fEmpObj -> fEmpObj.getAddress())));
return employeeMap;

4. Итерация через вложенную HashMap

Перебор вложенного Hashmap ничем не отличается от перебора обычного или не вложенного HashMap . Единственная разница между вложенной и обычной картой заключается в том, что значения вложенной карты HashMap относятся к типу карты :

for (Map.Entry<String, Map<Integer, String>> outerBakedGoodsMapEntrySet : outerBakedGoodsMap.entrySet()) {
Map<Integer, String> valueMap = outerBakedGoodsMapEntrySet.getValue();
System.out.println(valueMap.entrySet());
}

for (Map.Entry<Integer, Map<String, String>> employeeEntrySet : employeeAddressMap.entrySet()) {
Map<String, String> valueMap = employeeEntrySet.getValue();
System.out.println(valueMap.entrySet());
}

5. Сравнение вложенных HashMap

Есть много способов сравнить HashMap в Java. Мы можем сравнить их, используя метод equals() . Реализация по умолчанию сравнивает каждое значение.

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

assertNotEquals(outerBakedGoodsMap2, actualBakedGoodsMap);

outerBakedGoodsMap3.put("Donut", mUtil.buildInnerMap(batterList));
assertNotEquals(outerBakedGoodsMap2, actualBakedGoodsMap);

Map<Integer, Map<String, String>> employeeAddressMap1 = mUtil.createNestedMapfromStream(listEmployee);
assertNotEquals(employeeAddressMap1, actualEmployeeAddressMap);

Для Карты с пользовательскими объектами в качестве значений нам нужно настроить метод равенства, используя один из методов, упомянутых в статье сравнения HashMap . В противном случае проверки не пройдут:

//Comparing a Map<Integer, Map<String, String>> and Map<Integer, Map<Integer, Address>> map
assertNotSame(employeeMap1, actualEmployeeMap);
assertNotEquals(employeeMap1, actualEmployeeMap);
Map<Integer, Map<Integer, Address>> expectedMap = setupAddressObjectMap();
assertNotSame(expectedMap, actualEmployeeMap);
assertNotEquals(expectedMap, actualEmployeeMap);

Если обе карты одинаковы, то проверка на равенство проходит успешно. Для пользовательской карты, если все идентичные объекты перемещаются на другую карту, проверка на равенство завершается успешно:

Map<String, Map<Integer, String>> outerBakedGoodsMap4 = new HashMap<>();
outerBakedGoodsMap4.putAll(actualBakedGoodsMap);
assertEquals(actualBakedGoodsMap, outerBakedGoodsMap4);
Map<Integer, Map<Integer, Address>> employeeMap1 = new HashMap<>();
employeeMap1.putAll(actualEmployeeMap);
assertEquals(actualEmployeeMap, employeeMap1);

6. Добавление элементов во вложенные HashMap

Чтобы добавить элемент во внутреннюю карту вложенного HashMap , нам сначала нужно его получить. Мы можем получить внутренний объект, используя метод get() . Затем мы можем использовать метод put() для внутреннего объекта Map и вставить новые значения:

assertEquals(actualBakedGoodsMap.get("Cake").size(), 5);
actualBakedGoodsMap.get("Cake").put(6, "Cranberry");
assertEquals(actualBakedGoodsMap.get("Cake").size(), 6);

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

outerBakedGoodsMap.put("Eclair", new HashMap<Integer, String>() {
{
put(1, "Dark Chocolate");
}
});

7. Удаление записей из вложенных HashMap

Чтобы удалить запись из внутренней карты , сначала нам нужно получить ее, а затем использовать метод remove() для ее удаления. Если во внутренней Map есть только одно значение , то в качестве значения остается нулевой объект:

assertNotEquals(actualBakedGoodsMap.get("Cake").get(5), null);
actualBakedGoodsMap.get("Cake").remove(5);
assertEquals(actualBakedGoodsMap.get("Cake").get(5), null);
assertNotEquals(actualBakedGoodsMap.get("Eclair").get(1), null);
actualBakedGoodsMap.get("Eclair").remove(1);
assertEquals(actualBakedGoodsMap.get("Eclair").get(1), null);
actualBakedGoodsMap.put("Eclair", new HashMap<Integer, String>() {
{
put(1, "Dark Chocolate");
}
});

Если мы удалим запись из внешней карты , Java удалит как внутреннюю, так и внешнюю записи карты , что очевидно, поскольку внутренняя карта является «значением» внешней карты :

assertNotEquals(actualBakedGoodsMap.get("Eclair"), null);
actualBakedGoodsMap.remove("Eclair");
assertEquals(actualBakedGoodsMap.get("Eclair"), null);

8. Сглаживание вложенной HashMap

Одной из альтернатив вложенному HashMap является использование комбинированных ключей. Комбинированный ключ обычно объединяет два ключа из вложенной структуры с точкой между ними. Например, комбинированным ключом будет Donut.1 , Donut.2 и т. д. Мы можем «сгладить», т. е. преобразовать вложенную структуру Map в единую структуру Map :

var flattenedBakedGoodsMap = mUtil.flattenMap(actualBakedGoodsMap);
assertThat(flattenedBakedGoodsMap, IsMapContaining.hasKey("Donut.2"));
var flattenedEmployeeAddressMap = mUtil.flattenMap(actualEmployeeAddressMap);
assertThat(flattenedEmployeeAddressMap, IsMapContaining.hasKey("200.Bag End"));

Подход с комбинированными ключами устраняет недостатки дополнительного хранения памяти, связанные с вложенными HashMaps . Однако подход с комбинированными ключами не очень хорош для масштабирования.

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

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

Как всегда, код доступен на GitHub .