1. Overview
1.概述
In this tutorial, we’ll look at how to deal with nested HashMaps in Java. We’ll also see how to create and compare them. Finally, we’ll also see how to remove and add records to the inner maps.
在本教程中,我们将研究如何在Java中处理嵌套的HashMaps。我们还将看到如何创建和比较它们。最后,我们还将看到如何在内部映射中删除和添加记录。
2. Use Cases
2.使用案例
Nested HashMap is very helpful in storing JSON or JSON-like structures where objects are embedded inside one another. For example, a structure or JSON similar to:
嵌套的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" }
]
}
}
is a perfect candidate for a nested HashMap. In general, whenever we need to embed one object in another object, we can use them.
是一个嵌套HashMap的完美候选。一般来说,只要我们需要将一个对象嵌入到另一个对象中,就可以使用它们。
3. Create a HashMap
3.创建一个HashMap
There are multiple ways to create a HashMap, such as manually constructing the maps or using Streams and grouping functions. The Map structure can be both with primitive types as well as Objects.
有多种方法可以创建HashMap,例如手动构建地图或使用Streams和分组函数。Map结构既可以使用原始类型,也可以使用Objects。
3.1. Using the put() Method
3.1.使用put()方法
We can build a nested HashMap by manually creating the inner maps and then inserting them into the outer Map using the put method:
我们可以通过手动创建内部地图,然后使用put方法将其插入到外部Map中,从而构建一个嵌套的HashMap。
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;
}
We can test it with:
我们可以用以下方式进行测试。
assertThat(mUtil.buildInnerMap(batterList), is(notNullValue()));
Assert.assertEquals(actualBakedGoodsMap.keySet().size(), 2);
Assert.assertThat(actualBakedGoodsMap, IsMapContaining.hasValue(equalTo(mUtil.buildInnerMap(batterList))));
3.2. Using Streams
3.2.使用流
If we have a List that we want to convert to a Map, we can create a stream and then convert it to a Map using the Collectors.toMap method. Here, we have two examples: one has an inner Map of Strings, and the other is a Map with Integer and Object values.
如果我们有一个List想要转换为Map,我们可以创建一个流,然后使用Collectors.toMap方法将它转换为Map。在这里,我们有两个例子:一个有一个内部Map的字符串,另一个是有Integer和Object值的Map。
In the first example, the Employee has the Address object nested inside it. We’re then building a nested HashMap:
在第一个例子中,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;
In the second example, we are building an object of type <Employee id <Address id, Address object>>:
在第二个例子中,我们正在建立一个类型为<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. Iterating Through a Nested HashMap
4.遍历一个嵌套的HashMap
Iterating through a nested Hashmap is no different from iterating through a regular or unnested HashMap. The only difference between a nested and regular Map is that the values of a nested HashMap are Map type:
遍历一个嵌套的Hashmap与遍历一个普通的或未嵌套的HashMap没有什么不同。嵌套的和普通的Map之间的唯一区别是,嵌套的HashMap的值是Map类型。
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. Comparing Nested HashMaps
5.比较嵌套的HashMaps
There are many ways to compare HashMaps in Java. We can compare them using the equals() method. The default implementation compares each value.
在Java中,有许多方法可以比较HashMaps。我们可以使用equals()方法来比较它们。默认的实现是比较每个值。
If we change the inner Map’s contents, the equality check fails. If the inner objects are all new instances every time in the case of user-defined objects, the equality check will also fail. Similarly, if we change the outer Map‘s contents, the equality check will fail as well:
如果我们改变内部Map的内容,平等性检查就会失败。如果在用户定义对象的情况下,内部对象每次都是新的实例,那么平等检查也将失败。同样地,如果我们改变外部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);
For the Map with user-defined objects as values, we need to customize the equality method using one of the methods mentioned in the comparing HashMaps article. Otherwise, the checks will fail:
对于将用户定义的对象作为值的Map,我们需要使用比较HashMaps文章中提到的方法之一来定制平等方法。否则,检查将会失败。
//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);
If both the maps are the same, then the equality check succeeds. For a user-defined map, if all identical objects are moved into another map, the equality check succeeds:
如果两个地图都是相同的,那么平等检查就成功了。对于用户定义的地图,如果所有相同的对象都被移到另一个地图中,那么平等检查就成功了。
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. Adding Elements to Nested HashMaps
6.向嵌套的HashMaps添加元素
To add an element to the inner Map of the nested HashMap, we first have to retrieve it. We can retrieve the inner object using the get() method. Then we can use the put() method on the inner Map object and insert the new values:
要将一个元素添加到嵌套的Map的内部HashMap,我们首先要检索它。我们可以使用get()方法来检索内部对象。然后我们可以在内部Map对象上使用put()方法并插入新值。
assertEquals(actualBakedGoodsMap.get("Cake").size(), 5);
actualBakedGoodsMap.get("Cake").put(6, "Cranberry");
assertEquals(actualBakedGoodsMap.get("Cake").size(), 6);
If we have to add an entry to the outer Map, we need to supply the correct entries for the inner Maps as well:
如果我们必须向外部Map添加一个条目,我们也需要为内部Map提供正确的条目。
outerBakedGoodsMap.put("Eclair", new HashMap<Integer, String>() {
{
put(1, "Dark Chocolate");
}
});
7. Deleting Records from Nested HashMaps
7.从嵌套的HashMaps删除记录
To delete the record from the inner Map, first, we need to retrieve it and then use the remove() method to delete it. If there is only one value in the inner Map, then a null object is left as the value:
要从内部Map中删除记录,首先,我们需要检索它,然后使用remove()方法来删除它。如果内部Map中只有一个值,那么会留下一个null对象作为值。
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");
}
});
If we remove a record from the outer Map, Java deletes both, the inner and outer Map records, which is evident since the inner Map is the “value” of the outer Map:
如果我们从外部Map中删除一条记录,Java会同时删除内部和外部Map记录,这很明显,因为内部Map是外部Map的 “值”。
assertNotEquals(actualBakedGoodsMap.get("Eclair"), null);
actualBakedGoodsMap.remove("Eclair");
assertEquals(actualBakedGoodsMap.get("Eclair"), null);
8. Flatten a Nested HashMap
8.扁平化一个嵌套的HashMap
One alternative to a nested HashMap is to use combined keys. A combined key usually concatenates the two keys from the nested structure with a dot in between. For example, the combined key would be Donut.1, Donut.2, and so on. We can “flatten,” i.e., convert from nested Map structure to a single Map structure:
嵌套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"));
The combined keys approach overcomes the extra memory storage disadvantages that come with nested HashMaps. However, the combined keys approach is not very good at scaling.
组合键的方法克服了嵌套HashMaps所带来的额外内存存储的缺点。然而,组合键的方法在扩展方面不是很好。
9. Conclusion
9.结语
In this article, we saw how to create, compare, update and flatten a nested HashMap.
在这篇文章中,我们看到了如何创建、比较、更新和平整一个嵌套的HashMap。
As always, the code is available over on GitHub.
像往常一样,代码可在GitHub上获得。