【问题标题】:Find min/max element in a nested map with Java 8 Stream API使用 Java 8 Stream API 在嵌套地图中查找最小/最大元素
【发布时间】:2020-02-21 06:51:45
【问题描述】:

我有一个如下的数据结构:

Map<StudentStatus, TreeMap<Integer, Student>> map = new HashMap<>();

我可以要求像 PHD 这样的学生身份,它会给我一个整数到学生对象的 TreeMap。这个TreeMap 将学生 ID 作为键,将 Student 对象作为值。

public enum StudentStatus {
    UNDERGRAD,
    POSTGRAD,
    PHD;
}

public class Student {
    public String name;
    public int age;
}

public class StudentDetails {
    public int id;
    public StudentStatus studentStatus;
    public String name;
}

现在我想做的是,按年龄获取最年轻的学生,换句话说,搜索嵌套地图,并从该学生创建一个 StudentDetails 对象。 所以我需要前两张地图的钥匙。第一个键是 StudentStatus,第二个键是 Student id。

最后我需要一个这样的对象:

new StudentDetails(id: 36, studentStatus: StudentStatus.PHD, name: "Anil");

我找不到在流式传输时存储这些密钥并在单个语句中使用 Java 8 Stream 创建最终对象的方法。

我用Student类比简化了问题,也许对上述问题使用这样的结构没有多大意义,但在我的例子中确实如此。

【问题讨论】:

  • StudentEnumStudentStatus?
  • 刚刚修好了。谢谢。
  • 您可以将学生年龄添加到StudentDetails 班级作为成员吗?

标签: java dictionary java-8 java-stream


【解决方案1】:

构建Map.Entry,将年龄作为键,StudentDetails 作为值。然后排序,拿到第一。

StudentDetails studentDetails = map.entrySet().stream()
        .flatMap(statusEntry -> statusEntry.getValue().entrySet().stream()
                .map(ageEntry -> new AbstractMap.SimpleEntry<Integer, StudentDetails>(
                        ageEntry.getValue().getAge(),
                        new StudentDetails(
                                ageEntry.getKey(), 
                                statusEntry.getKey(), 
                                ageEntry.getValue().getName())))
        ).sorted(Comparator.comparingInt(Map.Entry::getKey))
        .findFirst()
        .map(Map.Entry::getValue)
        .get();

更新

正如在下面的评论中所写:最后一部分(来自“排序”) 可以用下面的代码替换:

        .min(Comparator.comparingInt(Map.Entry::getKey))
        .map(Map.Entry::getValue)
        .get();

【讨论】:

  • sort().findFirst() 的组合可以轻松替换为minmax
【解决方案2】:

如果你正在寻找对应的StudentDetails给定StudentStatus,你可以使用Stream#min,例如:

StudentDetails detailsForGivenStatus(Map<StudentStatus, TreeMap<Integer, Student>> map, StudentStatus studentStatus) {
    return map.get(studentStatus).entrySet().stream()
            .min(Comparator.comparing(e -> e.getValue().getAge()))
            .map(entry -> new StudentDetails(entry.getKey(), studentStatus, entry.getValue().getName()))
            .orElseThrow(IllegalArgumentException::new);
}

同样,如果您要查找基于每个状态的最年轻学生详细信息,您可以使用:

Map<StudentStatus, StudentDetails> statusToYoungestStudentDetail(Map<StudentStatus, TreeMap<Integer, Student>> map,
                                                                 StudentStatus studentStatus) {
    return map.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().entrySet()
                    .stream()
                    .min(Comparator.comparing(en -> en.getValue().getAge()))
                    .map(entry -> new StudentDetails(entry.getKey(),
                            studentStatus, entry.getValue().getName()))
                    .orElseThrow(IllegalArgumentException::new)));
}

【讨论】:

    【解决方案3】:

    此替代方案至少使用收集器;没有排序,运行时间是 O(n)。

    var student = map.values().stream()
        .flatMap(m -> m.entrySet().stream())
        .map(Map.Entry::getValue)
        .collect(Collectors.minBy(Student::getAge))
        .get();
    

    【讨论】:

    • 它返回 Student 对象而不是 StudentDetails。
    猜你喜欢
    • 1970-01-01
    • 2018-05-05
    • 2020-05-08
    • 1970-01-01
    • 2013-05-07
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多