Java 8 使用Java8流聚合列表对象

Java 8 使用Java8流聚合列表对象,java-8,Java 8,我们使用3个列表ListA、ListB、ListC为3个科目(A、B、C)的10名学生保留分数 科目B和C是选修的,所以10个学生中只有很少的学生在这些科目上有分数 Class Student{ String studentName; int marks; } ListA有10名学生的记录,ListB有5名,ListC有3名(这也是名单的大小) 想知道如何使用Java8STEAM总结学生的科目分数 我尝试了以下方法 List<Integer> list = IntStream.ra

我们使用3个列表ListA、ListB、ListC为3个科目(A、B、C)的10名学生保留分数

科目B和C是选修的,所以10个学生中只有很少的学生在这些科目上有分数

Class Student{
String studentName;
int marks;
}
ListA有10名学生的记录,ListB有5名,ListC有3名(这也是名单的大小)

想知道如何使用Java8STEAM总结学生的科目分数

我尝试了以下方法

List<Integer> list = IntStream.range(0,listA.size() -1).mapToObj(i -> listA.get(i).getMarks() + 
listB.get(i).getMarks() + 
listC.get(i).getMarks()).collect(Collectors.toList());;
List List=IntStream.range(0,listA.size()-1).mapToObj(i->listA.get(i).getMarks()+
listB.get(i).getMarks()+
listC.get(i.getMarks()).collect(collector.toList());;
这有两个问题

a) 它将提供IndexOutOfBoundsException,因为listB和listC没有10个元素

b) 返回的列表if的类型为Integer,我希望它的类型为Student


任何输入都将非常有用

您可以创建一个包含3个列表的流,然后调用
flatMap
将所有列表元素放入一个流中。该流将包含每个学生每个分数的一个元素,因此您必须按学生名称聚合结果。大致如下:

Map<String, Integer> studentMap = Stream.of(listA, listB, listC)
        .flatMap(Collection::stream)
        .collect(groupingBy(student -> student.name, summingInt(student -> student.mark)));
然后打印出
学生地图
,检查结果:

studentMap.forEach((key, value) -> System.out.println(key + " - " + value));
如果您想创建
Student
对象列表,则可以使用第一个映射的结果并从其条目创建一个新流(此特定示例假设您的
Student
类有一个all args构造函数,因此您可以将其一行):

List studentList=Stream.of(listA、listB、listC)
.flatMap(集合::流)
.collect(分组方式(学生::getName,汇总方式(学生::getMark)))
.entrySet().stream()
.map(mapEntry->new Student(mapEntry.getKey(),mapEntry.getValue())
.collect(toList());

我将按如下方式进行:

Map<String, Student> result = Stream.of(listA, listB, listC)
    .flatMap(List::stream)
    .collect(Collectors.toMap(
        Student::getName, // key: student's name
        s -> new Student(s.getName(), s.getMarks()), // value: new Student
        (s1, s2) -> { // merge students with same name: sum marks
            s1.setMarks(s1.getMarks() + s2.getMarks());
            return s1;
        }));
然后您可以用以下方式重写上面的代码:

Map<String, Student> result = Stream.of(listA, listB, listC)
    .flatMap(List::stream)
    .collect(Collectors.toMap(
        Student::getName,
        Student::new,
        Student::merge));
Map result=Stream.of(listA、listB、listC)
.flatMap(列表::流)
.collect(collector.toMap)(
学生::getName,
学生:新的,
学生::合并);

那么,一个科目实际上是一个学生,在一个科目上有分数,对吗?这个名字是学生的名字,对吗?我想你需要拉链,坦率地说,拉链不在标准图书馆里!!看这里了解如何做:是的,这是一个学生。我已经纠正了细节。谢谢你的回答。列表方法是灵活的,因为它可以容纳任意数量的必修和选修科目。如果
listA
listB
listC
是硬编码的,可以使用两次调用
Stream.concat()
将三个流连接成一个。@OleV.V。流(T…值)是3个或更多列表的任何其他选项<代码>流.of(listA,listb,listc).flatMap(Collection::Stream)如何返回学生对象所在的地图,而不是返回地图。mark将获得合计分数?据我所知,一旦您对流调用
collect
,流就完成了,所以您不能在一个流中完成所有操作。但是,您可以将调用链接在一起。我将更新我的答案。我更新了答案,以纳入@ÖmerErden的建议,并包括返回
列表的可能解决方案。如果对您更有用,您可以通过将最后一行替换为收集(toMap(Student::getName,Student->Student))
轻松地将其更改为映射。我同意,这是一种比我的解决方案更有效的方法。我建议你把这个作为公认的答案,@girish。这有优点也有缺点。我不喜欢你修改原始列表中的对象。询问者将不得不权衡这一点与优势。@OleV.V。在第一个代码段中,原始对象不是modified@FedericoPeraltaSchaffner,我相信调用
s1.setMarks(s1.getMarks()+s2.getMarks())
修改了
Student
对象
s1
,并且该对象来自原始流。如果我错了,请纠正我。@OleV.V。根据您的评论更新了答案,再次感谢
Map<String, Student> result = Stream.of(listA, listB, listC)
    .flatMap(List::stream)
    .collect(Collectors.toMap(
        Student::getName, // key: student's name
        s -> new Student(s.getName(), s.getMarks()), // value: new Student
        (s1, s2) -> { // merge students with same name: sum marks
            s1.setMarks(s1.getMarks() + s2.getMarks());
            return s1;
        }));
public Student(Student another) {
    this.name = another.name;
    this.marks = another.marks;
}

public Student merge(Student another) {
    this.marks += another.marks;
    return this;
}
Map<String, Student> result = Stream.of(listA, listB, listC)
    .flatMap(List::stream)
    .collect(Collectors.toMap(
        Student::getName,
        Student::new,
        Student::merge));