如何将多个Map与多个相同的键和列表组合为值?

tktrz96b  于 2021-06-29  发布在  Java
关注(0)|答案(3)|浏览(329)

我是java新手,我正在尝试合并多个以字符串为键、以列表为值的Map,以生成一个新的Map。

public class Student {
    private String name;
    private String country;

    //Setters and Getters
}

现在我有了一个util类,可以根据他们的国家将学生添加到列表中。

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
            ));
}

但是当我尝试合并两个Map时,我得到的是空Map。事实上,我需要有一个Map与三个键(印度,英国,美国)和他们的价值观是从多个Map列表合并w.r.t键。

j9per5c4

j9per5c41#

首先,从代码中删除以下调用:

MapAdder.clearStudentMap();

你正在清除 studentMap1 以及 studentMap2 .
当您这样做时:

studentMap1 = MapAdder.getStudentMap();

你得到了学生 Map 已存储。当你打电话给 clear Map上的方法

studentMap.clear();

你会清除所有的 Map 存储在同一内存引用上的条目。换言之,以下陈述

studentMap1 = MapAdder.getStudentMap();

不创建学生的副本 Map ,而它只是保存在变量上 studentMap1 对该Map的内存引用。
您的流方法几乎是正确的,请将其更改为:

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 方法)。在重复键的情况下,我们将Map值添加到左键的列表中。
顺便说一句,删除一些helper方法,因为它们会混淆代码,并使 addToMap 通过传递 Map 它本身作为参数,以便您可以将该方法与不同的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; }
                ));
    }
}
nzkunb0c

nzkunb0c2#

主要的问题是你一直在清除共享列表。需要创建独立的列表。
但是有一种比使用 MapAdder 班级。记住 country 也是学生班的一部分。所以只要提取它并使用流创建Map。
现在创建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,就可以用同样的方法创建组合Map了。只需使用每个Map的值,然后流式处理它们就可以得到学生示例。

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钥匙是作为学生课程的一部分。但是我们假设键独立于类。然后你可以用你的 mapAdder 建立原始Map。最终的Map可以用下面的 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;}));

学生班上有能手、二传手和托斯特林

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);
    }
}
7eumitmz

7eumitmz3#

在创建 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]

如果不再需要此扩展功能,可以将其删除并返回常规Map:

studentMap = new HashMap<>(studentMap);

另请参见:“contains”方法不适用于arraylist<int[]>,是否有其他方法?

相关问题