Java8中如何合并两个map?
如何处理Map含有重复元素的情况?
1. 初始化
我们定义两个map实例
private static Map<String, People> map1 = new HashMap<>();
private static Map<String, People> map2 = new HashMap<>();
People类
class People {
private Long id;
private String name;
public People(Long id, String name) {
this.id = id;
this.name = name;
}
......
}
然后往map中存入一些数据
static {
People people1 = new People(1L, "Henry");
map1.put(people1.getName(), people1);
People people2 = new People(2L, "Annie");
map1.put(people2.getName(), people2);
People people3 = new People(3L, "John");
map1.put(people3.getName(), people3);
People people4 = new People(4L, "George");
map2.put(people4.getName(), people4);
People people5 = new People(5L, "Henry");
map2.put(people5.getName(), people5);
}
特别需要注意的是people1 和 people5 在map中有完全相同的key(name)。
2. Map.merge()
Java8为 java.util.Map接口新增了merge()函数。
merge() 函数的作用是: 如果给定的key之前没设置value 或者value为null, 则将给定的value关联到这个key上.
否则,通过给定的remaping函数计算的结果来替换其value。如果remapping函数的计算结果为null,将解除此结果。
// 通过拷贝map1中的元素来构造一个新的HashMap
Map<String, People> map3 = new HashMap<>(map1);
// 引入merge函数和合并规则 map3.merge(key, value, (v1, v2) -> new People(v1.getId(), v2.getName())
// map2进行迭代将其元素合并到map3中
map2.forEach(
(key, value) -> map3.merge(key, value, (v1, v2) -> new People(v1.getId(), v2.getName())));
printMap("map3", map3);
/**
* map3:
* John:People{id=3, name='John'}
* Annie:People{id=2, name='Annie'}
* George:People{id=4, name='George'}
* Henry:People{id=1, name='Henry'}
*/
最终,通过结果可以看出,实现了两个map的合并,对重复的key也合并为同一个元素。
注意最后一个People的id来自map1而name来自map2.
原因是我们的merge函数的定义:
(v1, v2) -> new People(v1.getId(), v2.getName())
3. Stream.concat()
Java8的Stream API 也为解决该问题提供了较好的解决方案。
首先需要将两个map合为一个Stream。
Stream combined = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream());
我们需要将entry sets作为参数,然后利用Collectors.toMap():将结果放到新的map中。
try {
Map<String, People> map4 = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream()).collect(
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
printMap("map4", map4);
} catch (IllegalStateException e) {
System.out.println("Error: " + e);
}
该方法可以实现map的合并,但是有重复key会报IllegalStateException异常。
为了解决这个问题,我们需要加入lambda表达式merger作为第三个参数
(value1, value2) -> new Peopel(value2.getId(), value1.getName())
当检测到有重复Key时就会用到该lambda表达式。
Map<String, People> map5 = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(value1, value2) -> new People(value2.getId(), value1.getName())));
printMap("map5", map5);
/**
* map5:
* George:People{id=4, name='George'}
* John:People{id=3, name='John'}
* Annie:People{id=2, name='Annie'}
* Henry:People{id=5, name='Henry'}
*/
从结果可以看出重复的key “Henry”将合并为一个新的键值对,id取自map2,name取自map1。
4. Stream.of()
通过Stream.of()方法不需要借助其他stream就可以实现map的合并。
Map<String, People> map6 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> new People(v1.getId(), v2.getName())));
printMap("map6", map6);
/**
* map6:
* George:People{id=4, name='George'}
* John:People{id=3, name='John'}
* Annie:People{id=2, name='Annie'}
* Henry:People{id=1, name='Henry'}
*/
首先将map1和map2的元素合并为同一个流,然后再转成map。通过使用v1的id和v2的name来解决重复key的问题。
5. Simple Streaming
我们还可以借助stream的管道操作来实现map合并。
Map<String, People> map7 = map2.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> new People(v1.getId(), v2.getName()),
() -> new HashMap<>(map1)));
printMap("map7", map7);
/**
* map7:
* John:People{id=3, name='John'}
* Annie:People{id=2, name='Annie'}
* George:People{id=4, name='George'}
* Henry:People{id=1, name='Henry'}
*/
package learn;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class People {
private Long id;
private String name;
public People(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public class TestMergeMap {
private static Map<String, People> map1 = new HashMap<>();
private static Map<String, People> map2 = new HashMap<>();
static {
People people1 = new People(1L, "Henry");
map1.put(people1.getName(), people1);
People people2 = new People(2L, "Annie");
map1.put(people2.getName(), people2);
People people3 = new People(3L, "John");
map1.put(people3.getName(), people3);
People people4 = new People(4L, "George");
map2.put(people4.getName(), people4);
People people5 = new People(5L, "Henry");
map2.put(people5.getName(), people5);
}
public static void main(String[] args) {
// 通过拷贝map1中的元素来构造一个新的HashMap
Map<String, People> map3 = new HashMap<>(map1);
// 引入merge函数和合并规则 map3.merge(key, value, (v1, v2) -> new People(v1.getId(), v2.getName())
// map2进行迭代将其元素合并到map3中
map2.forEach(
(key, value) -> map3.merge(key, value, (v1, v2) -> new People(v1.getId(), v2.getName())));
printMap("map3", map3);
/**
* map3:
* John:People{id=3, name='John'}
* Annie:People{id=2, name='Annie'}
* George:People{id=4, name='George'}
* Henry:People{id=1, name='Henry'}
*/
try {
Map<String, People> map4 = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream()).collect(
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
printMap("map4", map4);
} catch (IllegalStateException e) {
System.out.println("Error: " + e);
}
Map<String, People> map5 = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(value1, value2) -> new People(value2.getId(), value1.getName())));
printMap("map5", map5);
/**
* map5:
* George:People{id=4, name='George'}
* John:People{id=3, name='John'}
* Annie:People{id=2, name='Annie'}
* Henry:People{id=5, name='Henry'}
*/
Map<String, People> map6 = Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> new People(v1.getId(), v2.getName())));
printMap("map6", map6);
/**
* map6:
* George:People{id=4, name='George'}
* John:People{id=3, name='John'}
* Annie:People{id=2, name='Annie'}
* Henry:People{id=1, name='Henry'}
*/
Map<String, People> map7 = map2.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> new People(v1.getId(), v2.getName()),
() -> new HashMap<>(map1)));
printMap("map7", map7);
/**
* map7:
* John:People{id=3, name='John'}
* Annie:People{id=2, name='Annie'}
* George:People{id=4, name='George'}
* Henry:People{id=1, name='Henry'}
*/
}
private static void printMap(String topic, Map<String, People> map) {
System.out.println(topic + ":");
map.forEach((key, value) -> {
System.out.println(key + ":" + value);
});
}
}