1. Introduction
1.介绍
In this quick tutorial, we’ll demonstrate how to merge two maps using the Java 8 capabilities.
在这个快速教程中,我们将演示如何使用Java 8的功能合并两个地图。
To be more specific, we’ll examine different merging scenarios including maps having duplicate entries.
更具体地说,我们将研究不同的合并情况,包括有重复条目的地图。
2. Initialization
2.初始化
As a start, let’s define two Map instances:
作为一个开始,让我们定义两个Map实例。
private static Map<String, Employee> map1 = new HashMap<>();
private static Map<String, Employee> map2 = new HashMap<>();
The Employee class looks like this:
Employee类看起来像这样。
public class Employee {
private Long id;
private String name;
// constructor, getters, setters
}
Then, we can push some data into the Map instances:
然后,我们可以向Map实例推送一些数据。
Employee employee1 = new Employee(1L, "Henry");
map1.put(employee1.getName(), employee1);
Employee employee2 = new Employee(22L, "Annie");
map1.put(employee2.getName(), employee2);
Employee employee3 = new Employee(8L, "John");
map1.put(employee3.getName(), employee3);
Employee employee4 = new Employee(2L, "George");
map2.put(employee4.getName(), employee4);
Employee employee5 = new Employee(3L, "Henry");
map2.put(employee5.getName(), employee5);
Note, that we have identical keys for employee1 and employee5 entries in our maps which we’ll use later.
注意,我们的地图中employee1和employee5条目有相同的键,我们将在后面使用。
3. Map.merge()
3.Map.merge()
Java 8 adds a new merge() function into the java.util.Map interface.
Java 8在java.util.Map接口中增加了一个新的merge()函数。
Here is how the merge() function works: If the specified key is not already associated with a value or the value is null, it associates the key with the given value.
下面是merge()函数的工作方式。如果指定的键没有与一个值相关联,或者该值是空的,它就将该键与给定的值相关联。
Otherwise, it replaces the value with the results of the given remapping function. If the result of the remapping function is null, it removes the result.
否则,它将用给定的重映射函数的结果替换该值。如果重映射函数的结果是空的,它将删除该结果。
First, let’s construct a new HashMap by copying all the entries from the map1:
首先,让我们构建一个新的HashMap,从map1中复制所有条目。
Map<String, Employee> map3 = new HashMap<>(map1);
Next, let’s introduce the merge() function along with merging rule:
接下来,让我们介绍一下merge()函数和合并规则。
map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())
Finally, we’ll iterate over the map2 and merge the entries into map3:
最后,我们将遍历map2并将条目合并到map3。
map2.forEach(
(key, value) -> map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())));
Let’s run the program and print the content of map3:
让我们运行该程序,打印map3的内容。
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
George=Employee{id=2, name='George'}
Henry=Employee{id=1, name='Henry'}
As a result, our combined Map has all the elements of the previous HashMap entries. Entries with duplicate keys have been merged into one entry.
因此,我们合并后的Map拥有之前HashMap条目的所有元素。键值重复的条目已被合并为一个条目。
Also, we notice that the Employee object of the last entry has the id from the map1, and value is picked from map2.
另外,我们注意到,最后一个条目的Employee对象的id来自map1,而值则来自map2。
This is because of the rule we defined in our merger function:
这是因为我们在合并函数中定义的规则。
(v1, v2) -> new Employee(v1.getId(), v2.getName())
4. Stream.concat()
4.Stream.concat()
The Stream API in Java 8 can also provide an easy solution to our problem. First, we need to combine our Map instances into one Stream. That’s exactly what Stream.concat() operation does:
Java 8中的Stream API也可以为我们的问题提供一个简单的解决方案。首先,我们需要将我们的Map实例合并成一个Stream。这正是Stream.concat()操作的目的。
Stream combined = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream());
Here we pass the map entry sets as parameters. Next, we need to collect our result into a new Map. For that we can use Collectors.toMap():
在这里,我们将地图条目集作为参数传递。接下来,我们需要将我们的结果收集到一个新的Map。为此,我们可以使用Collectors.toMap()。
Map<String, Employee> result = combined.collect(
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
As a result, the collector will use the existing keys and values of our maps. But this solution is far from being perfect. As soon as our collector meets entries with duplicate keys, it’ll throw an IllegalStateException.
因此,收集器将使用我们地图的现有键和值。但这个解决方案远非完美。一旦我们的收集器遇到键值重复的条目,它就会抛出一个IllegalStateException。
To handle this issue, we simply add a third “merger” lambda parameter into our collector:
为了处理这个问题,我们只需在收集器中添加第三个 “合并 “lambda参数。
(value1, value2) -> new Employee(value2.getId(), value1.getName())
It will use the lambda expression every time a duplicate key is detected.
它将在每次检测到重复的键时使用lambda表达式。
Finally, putting all together:
最后,把所有的东西放在一起。
Map<String, Employee> result = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(value1, value2) -> new Employee(value2.getId(), value1.getName())));
Finally, let’s run the code and see the results:
最后,让我们运行代码,看看结果。
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Henry=Employee{id=3, name='Henry'}
As we see, the duplicate entries with the key “Henry” were merged into a new key-value pair where the id of the new Employee was picked from the map2 and the value from map1.
正如我们所看到的,键值为“Henry”的重复条目被合并成一个新的键值对,其中新的Employee的id是从map2中挑选出来的,其值是从map1中挑选出来的。
5. Stream.of()
5.流.of()
To continue to use the Stream API, we can turn our Map instances into a unified stream with the help of Stream.of().
为了继续使用Stream API,我们可以在Stream.of()的帮助下将我们的Map实例变成一个统一的流。
Here we don’t have to create an additional collection to work with the streams:
在这里,我们不需要创建一个额外的集合来处理流的问题。
Map<String, Employee> map3 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> new Employee(v1.getId(), v2.getName())));
First, we transform map1 and map2 into a single stream. Next, we convert the stream into the map. As we can see, the last argument of toMap() is a merging function. It solves the duplicate keys problem by picking the id field from v1 entry, and the name from v2.
首先,我们将map1和map2转换成一个流。接下来,我们将流转换为地图。我们可以看到,toMap()的最后一个参数是一个合并函数。它通过从v1条目中选取id字段,从v2中选取名称,解决了重复键的问题。
The printed map3 instance after running the program:
运行程序后打印的map3实例。
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Henry=Employee{id=1, name='Henry'}
6. Simple Streaming
6.简单的流媒体
Additionally, we can use a stream() pipeline to assemble our map entries. The code snippet below demonstrates how to add the entries from map2 and map1 by ignoring the duplicate entries:
此外,我们可以使用stream() 管道来集合我们的地图条目。下面的代码片段演示了如何通过忽略重复的条目来添加来自map2和map1的条目。
Map<String, Employee> map3 = map2.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> new Employee(v1.getId(), v2.getName()),
() -> new HashMap<>(map1)));
As we expect, the results after the merge are:
如我们所料,合并后的结果是。
{John=Employee{id=8, name='John'},
Annie=Employee{id=22, name='Annie'},
George=Employee{id=2, name='George'},
Henry=Employee{id=1, name='Henry'}}
7. StreamEx
7.StreamEx
In addition to solutions that are provided by the JDK, we can also use the popular StreamEx library.
除了JDK提供的解决方案外,我们还可以使用流行的StreamEx库。
Simply put, StreamEx is an enhancement for the Stream API and provides many additional useful methods. We’ll use an EntryStream instance to operate on key-value pairs:
简单地说,StreamEx 是对Stream API的增强,提供了许多额外的有用方法。我们将使用一个EntryStream实例来操作键值对。
Map<String, Employee> map3 = EntryStream.of(map1)
.append(EntryStream.of(map2))
.toMap((e1, e2) -> e1);
The idea is to merge the streams of our maps into one. Then we collect the entries into the new map3 instance. Important to mention, the (e1, e2) -> e1 expression as it helps to define the rule for dealing with the duplicate keys. Without it, our code will throw an IllegalStateException.
我们的想法是将我们的地图流合并成一个。然后我们把这些条目收集到新的map3实例中。值得一提的是,(e1, e2) -> e1表达式,它有助于定义处理重复键的规则。没有它,我们的代码将抛出一个IllegalStateException。
And now, the results:
现在,结果出来了。
{George=Employee{id=2, name='George'},
John=Employee{id=8, name='John'},
Annie=Employee{id=22, name='Annie'},
Henry=Employee{id=1, name='Henry'}}
8. Summary
8.总结
In this short article, we learned different ways of merging maps in Java 8. More specifically, we used Map.merge(), Stream API, StreamEx library.
在这篇短文中,我们学习了Java 8中合并地图的不同方法。更具体地说,我们使用了Map.merge()、Stream API、StreamEx库。
As always, the code used during the discussion can be found over on GitHub.
一如既往,讨论中所使用的代码可以在GitHub上找到over。