使用 java 8 流获得平均主题分数的最大值
Get maximum of average subject marks using java 8 stream
我有 class 个学生如下
class Student{
Map<String,Integer> subjectMarks;
String name;
public Student(Map<String,Integer> subject, String name) {
super();
this.subjectMarks = subject;
this.name = name;
}
public Map<String,Integer> getSubjectMarks() {
return subjectMarks;
}
public void setSubjectMarks(Map<String,Integer> subject) {
this.subjectMarks = subject;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在 main 方法中,我们将学生对象添加到 arraylist 中,如下所示。
ArrayList<Student> arr = new ArrayList<Student>();
Map m1 = new HashedMap();
m1.put("Maths",40);
m1.put("Science",50);
Map m2 = new HashedMap();
m2.put("Maths",60);
m2.put("Science",20);
arr.add(new Student(m1, "RAJ"));
arr.add(new Student(m2, "AMIT"));
我可以 help/guide 找出每个学生的科目平均分,然后从平均分中得到最大值。我需要帮助在 java8
中编写此代码段
不要将自己局限于 Java8 中的流的想法,您必须将流结果直接处理到下一个流等等...效率可能不是最好的,但考虑嵌套循环。
开始想想你有什么:每个 Student
都有几个分数。您想要为每个 Student
找到这些标记的平均值。您可以将问题减少到首先考虑如何获得一个 Student
.
的平均值
double average = student.getSubjectMarks().values().stream()
.mapToInt(Integer::valueOf).average().orElse(0.0);
即使您的示例仅显示整数,平均值也可以是浮点数。
然后,您将不得不遍历所有学生并为每个学生执行上述过程。
Map<String, Double> studentAverages = new HashMap<>();
arr.forEach(student -> {
double average = student.getSubjectMarks().values().stream()
.mapToInt(Integer::valueOf).average().orElse(0.0);
studentAverages.put(student.getName(), average);
});
在所描述的实现中,所需的平均值保存在 Map
studentAverages
中,其中以学生的姓名作为键,将平均分数作为值。
然后您可以简单地从列表中获取最大整数。
studentAverages.values().stream().mapToDouble(Double::doubleValue).max();
一些答案提供了更复杂的流用法。但是,上面的代码更具可读性。此外,数据类型Object
非常通用,难以进一步使用且容易出错。
正如@Felix 在他的回答中指出的那样,由于您有一个嵌套集合,因此很难在单个流中进行处理。您可以使用一个流来计算每个学生的平均值,并使用另一个流来计算平均值的最大值。
从一个单独的函数开始计算学生的平均分:
private OptionalDouble calculateAverageMarks(Student student) {
return student.getSubjectMarks().values().stream()
.mapToInt(Integer::intValue)
.average();
}
请注意,如果您想要 return 和 double
,您可以将 .orElse(0.0)
(或其他一些值)添加到管道中,但这不允许您稍后区分在全 0 的学生和未注册任何科目的学生之间。
然后您可以将平均值收集到地图中。
Map<String, OptionalDouble> averageMarks = arr.stream()
.collect(Collectors.toMap(Student::getName, this::calculateAverageMarks));
请注意,如果两个学生同名,在收集器中使用 Student::getName
将会抛出异常。您可以使用 Function.identity()
来确保每个键都是不同的,只要您不覆盖 Student
.
中的 equals
如果需要,您可以删除没有科目的学生
averageMarks.values().removeIf(v -> !v.isPresent());
在 Java 11 中,您可以使用 OptionalDouble::isEmpty
代替 lambda。
然后你可以将值映射到双流并获得最大值
OptionalDouble max = averageMarks.values().stream()
.filter(OptionalDouble::isPresent) // In case you didn't remove the empty marks
.mapToDouble(OptionalDouble::getAsDouble)
.max();
按照您的要求使用流来解决这个问题没有错。
- 流式传输地图列表。
- 将学生姓名和平均分放入一个对象数组中。
- 流式传输地图的值并使用 summaryStatistics 获取平均值。
- 然后将 maxBy 与比较器一起使用以获得最大平均值。
- 然后显示出来
Object[] result = arr.stream().map(s -> new Object[] {
s.getName(),
s.getSubjectMarks().values().stream()
.mapToInt(Integer::valueOf)
.summaryStatistics().getAverage() })
.max(Comparator.comparing(obj -> (double) obj[1]))
.get();
System.out.println(Arrays.toString(result));
版画
[RAJ, 45.0]
我有 class 个学生如下
class Student{
Map<String,Integer> subjectMarks;
String name;
public Student(Map<String,Integer> subject, String name) {
super();
this.subjectMarks = subject;
this.name = name;
}
public Map<String,Integer> getSubjectMarks() {
return subjectMarks;
}
public void setSubjectMarks(Map<String,Integer> subject) {
this.subjectMarks = subject;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在 main 方法中,我们将学生对象添加到 arraylist 中,如下所示。
ArrayList<Student> arr = new ArrayList<Student>();
Map m1 = new HashedMap();
m1.put("Maths",40);
m1.put("Science",50);
Map m2 = new HashedMap();
m2.put("Maths",60);
m2.put("Science",20);
arr.add(new Student(m1, "RAJ"));
arr.add(new Student(m2, "AMIT"));
我可以 help/guide 找出每个学生的科目平均分,然后从平均分中得到最大值。我需要帮助在 java8
中编写此代码段不要将自己局限于 Java8 中的流的想法,您必须将流结果直接处理到下一个流等等...效率可能不是最好的,但考虑嵌套循环。
开始想想你有什么:每个 Student
都有几个分数。您想要为每个 Student
找到这些标记的平均值。您可以将问题减少到首先考虑如何获得一个 Student
.
double average = student.getSubjectMarks().values().stream()
.mapToInt(Integer::valueOf).average().orElse(0.0);
即使您的示例仅显示整数,平均值也可以是浮点数。
然后,您将不得不遍历所有学生并为每个学生执行上述过程。
Map<String, Double> studentAverages = new HashMap<>();
arr.forEach(student -> {
double average = student.getSubjectMarks().values().stream()
.mapToInt(Integer::valueOf).average().orElse(0.0);
studentAverages.put(student.getName(), average);
});
在所描述的实现中,所需的平均值保存在 Map
studentAverages
中,其中以学生的姓名作为键,将平均分数作为值。
然后您可以简单地从列表中获取最大整数。
studentAverages.values().stream().mapToDouble(Double::doubleValue).max();
一些答案提供了更复杂的流用法。但是,上面的代码更具可读性。此外,数据类型Object
非常通用,难以进一步使用且容易出错。
正如@Felix 在他的回答中指出的那样,由于您有一个嵌套集合,因此很难在单个流中进行处理。您可以使用一个流来计算每个学生的平均值,并使用另一个流来计算平均值的最大值。
从一个单独的函数开始计算学生的平均分:
private OptionalDouble calculateAverageMarks(Student student) {
return student.getSubjectMarks().values().stream()
.mapToInt(Integer::intValue)
.average();
}
请注意,如果您想要 return 和 double
,您可以将 .orElse(0.0)
(或其他一些值)添加到管道中,但这不允许您稍后区分在全 0 的学生和未注册任何科目的学生之间。
然后您可以将平均值收集到地图中。
Map<String, OptionalDouble> averageMarks = arr.stream()
.collect(Collectors.toMap(Student::getName, this::calculateAverageMarks));
请注意,如果两个学生同名,在收集器中使用 Student::getName
将会抛出异常。您可以使用 Function.identity()
来确保每个键都是不同的,只要您不覆盖 Student
.
equals
如果需要,您可以删除没有科目的学生
averageMarks.values().removeIf(v -> !v.isPresent());
在 Java 11 中,您可以使用 OptionalDouble::isEmpty
代替 lambda。
然后你可以将值映射到双流并获得最大值
OptionalDouble max = averageMarks.values().stream()
.filter(OptionalDouble::isPresent) // In case you didn't remove the empty marks
.mapToDouble(OptionalDouble::getAsDouble)
.max();
按照您的要求使用流来解决这个问题没有错。
- 流式传输地图列表。
- 将学生姓名和平均分放入一个对象数组中。
- 流式传输地图的值并使用 summaryStatistics 获取平均值。
- 然后将 maxBy 与比较器一起使用以获得最大平均值。
- 然后显示出来
Object[] result = arr.stream().map(s -> new Object[] {
s.getName(),
s.getSubjectMarks().values().stream()
.mapToInt(Integer::valueOf)
.summaryStatistics().getAverage() })
.max(Comparator.comparing(obj -> (double) obj[1]))
.get();
System.out.println(Arrays.toString(result));
版画
[RAJ, 45.0]