我是 Java 新手,我正在尝试合并多个以字符串作为键、列表作为值的映射,以生成一个新的 Map。
public class Student {
private String name;
private String country;
//Setters and Getters
}
现在我有一个实用类,可以根据学生所在国家/地区将学生添加到列表中。
public class MapAdder {
static Map<String, List<Student>> studentMap =
new LinkedHashMap<String, List<Student>>();
public static void addToMap(String key, Student student) {
studentMap.computeIfAbsent(key,
k -> new LinkedList<Student>()).add(student);
}
public static Map<String, List<Student>> getStudentMap() {
return studentMap;
}
public static void clearStudentMap() {
studentMap.clear();
}
}
主要方法
public static void main(String[] args) {
Map<String, List<Student>> studentMap1;
Map<String, List<Student>> studentMap2;
Map<String, List<Student>> studentMap3;
MapAdder.addToMap("India", new Student("Mounish", "India"));
MapAdder.addToMap("USA", new Student("Zen", "USA"));
MapAdder.addToMap("India", new Student("Ram", "India"));
MapAdder.addToMap("USA", new Student("Ronon", "USA"));
MapAdder.addToMap("UK", new Student("Tony", "UK"));
studentMap1 = MapAdder.getStudentMap();
MapAdder.clearStudentMap();
MapAdder.addToMap("India", new Student("Rivar", "India"));
MapAdder.addToMap("UK", new Student("Loki", "UK"));
MapAdder.addToMap("UK", new Student("Imran", "UK"));
MapAdder.addToMap("USA", new Student("ryan", "USA"));
studentMap2 = MapAdder.getStudentMap();
MapAdder.clearStudentMap();
Map<String, List<Student>> map3 = Stream.of(studentMap1, studentMap2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Entry::getKey,
Entry::getValue
));
}
但是当我尝试合并两个地图时,我得到的是空地图。 实际上,我需要一张包含三个键(印度、英国、美国)的地图,以及它们的值,这些值是从多个地图中列出的,以便与键合并。
首先,从代码中删除以下调用:
MapAdder.clearStudentMap();
您正在清除
studentMap1
和 studentMap2
。
当你这样做时:
studentMap1 = MapAdder.getStudentMap();
您将获得存储学生
Map
的内存引用。当您在该地图上调用 clear
方法时
studentMap.clear();
您将清除存储在同一内存引用上的所有
Map
条目。换句话说,下面的语句
studentMap1 = MapAdder.getStudentMap();
不会创建学生
Map
的副本,而是仅将对该 Map 的内存引用保存在变量 studentMap1
上。
你的 Stream 方法几乎正确,将其更改为:
Map<String, List<Student>> map3 = Stream.of(studentMap1, studentMap2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> new ArrayList<>(e.getValue()),
(left, right) -> { left.addAll(right); return left; }
));
您还需要添加用于处理重复键的策略(即
mergeFunction
方法的Collectors.toMap
参数)。如果出现重复的键,我们将映射值添加到 left 键的列表中。
顺便说一句,在我看来,删除了其中一些辅助方法,它们混淆了代码,并通过将
addToMap
本身作为参数传递,使 Map
方法更加通用,以便您可以在不同的映射器中重用该方法,即:
public class MapAdder {
public static void addToMap(Map<String, List<Student>> studentMap,
String key, Student student) {
studentMap.computeIfAbsent(key,
k -> new LinkedList<Student>()).add(student);
}
public static void main(String[] args) {
Map<String, List<Student>> studentMap1 = new LinkedHashMap<>();
Map<String, List<Student>> studentMap2 = new LinkedHashMap<>();
Map<String, List<Student>> studentMap3;
MapAdder.addToMap(studentMap1, "India", new Student("Mounish", "India"));
MapAdder.addToMap(studentMap1, "USA", new Student("Zen", "USA"));
MapAdder.addToMap(studentMap1, "India", new Student("Ram", "India"));
MapAdder.addToMap(studentMap1, "USA", new Student("Ronon", "USA"));
MapAdder.addToMap(studentMap1, "UK", new Student("Tony", "UK"));
MapAdder.addToMap(studentMap2, "India", new Student("Rivar", "India"));
MapAdder.addToMap(studentMap2, "UK", new Student("Loki", "UK"));
MapAdder.addToMap(studentMap2, "UK", new Student("Imran", "UK"));
MapAdder.addToMap(studentMap2, "USA", new Student("ryan", "USA"));
Map<String, List<Student>> map3 = Stream.of(studentMap1, studentMap2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> new ArrayList<>(e.getValue()),
(left, right) -> { left.addAll(right); return left; }
));
}
}
HashMap
实例时,您可以重写其 put
和 putAll
方法,这样它们就不会替换现有值,而是附加它们,即合并相同值的值列表按键:
Map<String, List<Student>> studentMap = new HashMap<>() {
@Override
public List<Student> put(String key, List<Student> value) {
if (this.containsKey(key)) {
List<Student> val = this.get(key);
val.addAll(value);
return val;
} else {
return super.put(key, new ArrayList<>(value));
}
}
@Override
public void putAll(Map<? extends String, ? extends List<Student>> m) {
Iterator<? extends Entry<? extends String, ? extends List<Student>>>
iterator = m.entrySet().iterator();
while (iterator.hasNext()) {
Entry<? extends String, ? extends List<Test.Student>>
e = iterator.next();
this.put(e.getKey(), e.getValue());
}
}
};
studentMap.put("India", List.of(new Student("Mounish", "India")));
studentMap.put("USA", List.of(new Student("Zen", "USA")));
studentMap.putAll(Map.of(
"India", List.of(new Student("Ram", "India")),
"USA", List.of(new Student("Ronon", "USA")),
"UK", List.of(new Student("Tony", "UK"))));
studentMap.putAll(Map.of(
"India", List.of(new Student("Rivar", "India")),
"UK", List.of(new Student("Loki", "UK"))));
studentMap.putAll(Map.of(
"UK", List.of(new Student("Imran", "UK")),
"USA", List.of(new Student("ryan", "USA"))));
studentMap.forEach((k, v) -> System.out.println(k + "=" + v));
// USA=[Zen:USA, Ronon:USA, ryan:USA]
// UK=[Tony:UK, Loki:UK, Imran:UK]
// India=[Mounish:India, Ram:India, Rivar:India]
如果您不再需要此扩展功能,您可以将其删除并返回常规地图:
studentMap = new HashMap<>(studentMap);
主要问题是你不断清除共享列表。 需要创建独立的列表。
但是有一种比使用
MapAdder
类更简单的方法来添加值。 请记住,country
也是学生班级的一部分。 因此,只需提取它并使用流创建地图即可。
现在创建studentMap1
List<Student> list1 = List.of(
new Student("Mounish", "India"),
new Student("Zen", "USA"),
new Student("Ram", "India"),
new Student("Ronon", "USA"),
new Student("Tony", "UK"));
Map<String, List<Student>> studentMap1 =
list1.stream().collect(Collectors.groupingBy(Student::getCountry));
studentMap1.entrySet().forEach(System.out::println);
打印
USA=[{Zen, USA}, {Ronon, USA}]
UK=[{Tony, UK}]
India=[{Mounish, India}, {Ram, India}]
现在创建studentMap2
List<Student> list2 = List.of(
new Student("Rivar", "India"),
new Student("Loki", "UK"),
new Student("Imran", "UK"),
new Student("ryan", "USA"));
Map<String, List<Student>> studentMap2 =
list2.stream().collect(Collectors.groupingBy(Student::getCountry));
studentMap2.entrySet().forEach(System.out::println);
打印
USA=[{ryan, USA}]
UK=[{Loki, UK}, {Imran, UK}]
India=[{Rivar, India}]
现在您已经有了地图,您可以以相同的方式创建组合地图。 只需使用每个地图的值,然后对它们进行流式处理即可获取学生实例。
Map<String, List<Student>> map3 = Stream.of(studentMap1,studentMap2)
.map(Map::values) // values which is a collection of lists
.flatMap(Collection::stream) // flat map the two collections
.flatMap(Collection::stream) // flat map the lists to just
// a stream of students
.collect(Collectors.groupingBy(Student::getCountry));
map3.entrySet().forEach(System.out::println);
打印
USA=[{Zen, USA}, {Ronon, USA}, {ryan, USA}]
UK=[{Tony, UK}, {Loki, UK}, {Imran, UK}]
India=[{Mounish, India}, {Ram, India}, {Rivar, India}]
您很幸运,Map 键已包含在 Student 类中。 但我们假设密钥独立于类。 然后你可以使用你的
mapAdder
来构建原始地图。 最终的地图可以使用下面的 merge
函数来创建,用于合并重复的键。
Map<String, List<Student>> map4 =
Stream.of(studentMap1, studentMap2)
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Entry::getKey,
e -> new ArrayList<>(e.getValue),
(lst1, lst2) -> {lst1.addAll(lst2); return lst1;}));
具有 getter 和 setter 以及 toString 的学生类
class Student {
private String name;
private String country;
public Student(String name, String country) {
this.name = name;
this.country = country;
}
public String getName() {
return name;
}
public String getCountry() {
return country;
}
@Override
public String toString() {
return String.format("{%s, %s}", name, country);
}
}