Java 我应该在一个循环中使用多个流吗?

Java 我应该在一个循环中使用多个流吗?,java,loops,java-stream,coding-efficiency,Java,Loops,Java Stream,Coding Efficiency,假设我们正在与这个班一起工作: public Student{ String name; Integer age; Integer height; Integer weight; } 现在我们有了一份学生名单,我们被问到如下问题: 过滤那些名字叫“Jonh”的人,得到平均年龄 过滤那些名字叫“玛丽”的人,获得最高分数 过滤那些名字叫“Ben”的人,得到最小的权重 我认为一个干净易懂的解决方案是使用lambdas按名称进行过滤,并获得所需的: List<Student&

假设我们正在与这个班一起工作:

public Student{
  String name;
  Integer age;
  Integer height;
  Integer weight;
}
现在我们有了一份学生名单,我们被问到如下问题:

  • 过滤那些名字叫“Jonh”的人,得到平均年龄
  • 过滤那些名字叫“玛丽”的人,获得最高分数
  • 过滤那些名字叫“Ben”的人,得到最小的权重
我认为一个干净易懂的解决方案是使用lambdas按名称进行过滤,并获得所需的:

List<Student> students = ...
double jonhAge = students.stream().filter(s->s.getName().equals("Jonh").mapToInt(s->s.getAge()).average()
double maryHeight = students.stream().filter(s->s.getName().equals("Mary").mapToInt(s->s.getHeight()).max()
double benWeight = students.stream().filter(s->s.getName().equals("Ben").mapToInt(s->s.getWeight()).min()
列出学生=。。。
double jonhAge=students.stream().filter(s->s.getName().equals(“Jonh”).mapToInt(s->s.getAge()).average()
double maryHeight=students.stream().filter(s->s.getName().equals(“Mary”).mapToInt(s->s.getHeight()).max()
double benWeight=students.stream().filter(s->s.getName().equals(“Ben”).mapToInt(s->s.getWeight()).min()
我猜它至少在列表上迭代了3次,使用带条件的单个循环可能更有效:

double jonhAge = 0;
double jonhCount = 0;
double maryHeight = 0;
double benWeight = 1000;
for(Student s : students){
  if(s.getName.equals("Jonh")){
    jonhAge += s.getAge();
    jonhCount++;
  }else if(s.getName.equals("Mary")){
    if(s.getHeight()>maryHeight)
      maryHeight = s.getHeight();
  }else if(s.getName.equals("Ben")){
    if(s.getWeight()<benWeight )
      benWeight = s.getWeight();
  }
}
jonhAge = jonhAge / jonhCount;
double jonhAge=0;
双jonhCount=0;
双maryHeight=0;
双倍重量=1000;
(学生:学生){
如果(s.getName.equals(“Jonh”)){
jonhAge+=s.getAge();
jonhCount++;
}否则如果(s.getName.equals(“玛丽”)){
如果(s.getHeight()>maryHeight)
maryHeight=s.getHeight();
}否则如果(s.getName.equals(“Ben”)){

如果(s.getWeight(),我认为您考虑的问题是正确的。您当然可以尝试循环,以避免在同一数据流上进行多次传递。不过,也有更有趣的流方式来实现这一点

List<String> goodNames = ...put in names you want to see...;
Map<String, List<Student>> postsPerName = students.stream().filter(goodNames::contains).collect(groupingBy(Student::getName));
List goodNames=…输入您想看到的名称。。。;
Map postsPerName=students.stream().filter(goodNames::contains).collect(groupingBy(Student::getName));
然后你就有了一个地图,上面只有你关心的名字,所有的元素都与它们的名字相匹配


这是“完美”的解决方案吗?也许,也许不是。但这是一种平衡。祝你好运!

这可以通过以下方式使用流API实现:

  • 创建一个名映射到
    Student
    类的相应获取者
  • IntSummaryStatistics
    类的getter创建另一个名称映射
  • 使用
    收集器构建地图。groupingBy
    +
    收集器。SummaringIT
    获取每个学生的统计信息,并使用
    收集器转换此地图。toMap
    获取学生所需的统计信息参数
  • 更新
    可以实现枚举来存储对
    Student
    IntSummaryStatistics
    中适当getter的引用,然后映射应该使用枚举值

    enum FieldStat{
    平均年龄(学生::getAge,综合统计::getAverage),
    高度_MAX(学生::getHeight,IntSummaryStatistics::getMax),
    权重\最小值(学生::getWeight,IntSummaryStatistics::getMin);
    私有最终ToIntFunction字段;
    私人最终功能统计;
    FieldStat(ToIntFunction getter、Function stat){
    this.field=getter;
    this.stat=stat;
    }
    public-ToIntFunction-getField(){
    返回字段;
    }
    公共函数getStat(){
    返回统计;
    }
    }
    
    实施示例:

    List students=Arrays.asList(
    新生(“约翰”,35,177,78),新生(“约翰”,29,173,72),
    新生(“玛丽”,32,164,58),新生(“玛丽”,28,167,56),
    新生(“本”,2418184),新生(“本”,2716965),
    新生(“鲍勃”,35,178,80)
    );
    Map mapStat=Map.of(
    “约翰”,FieldStat.平均年龄,
    “玛丽”,FieldStat.HEIGHT_MAX,
    “本”,现场统计重量
    );
    Map stats=students.stream()
    .filter(st->mapStat.containsKey(st.getName()))
    .collect(收集器.groupingBy(
    学生::getName,
    Collectors.summaringint(st->mapStat.get(st.getName()).getField().applyAsInt(st))
    ))
    .entrySet().stream()
    .collect(collector.toMap)(
    Map.Entry::getKey,
    e->mapStat.get(e.getKey()).getStat().apply(e.getValue())
    ));
    系统输出打印项次(统计);
    
    输出

    {John=32.0, Ben=65, Mary=167}
    

    回答得很好!但我想知道为什么这个解决方案比遍历列表更有效。这不是一个“如何做”的问题,而是一个“哪种方式更好,为什么?”。谢谢!您可以创建一个包含名称和函数的枚举,而不是getter的两个单独映射。性能与按列表迭代相当,因为输入列表只迭代一次。此解决方案的主要优点是,它允许以声明方式通过向地图无需更改太多代码。此外,单个映射可以使用值来实现,这些值组合了对
    Student
    IntSummaryStatistics
    @daniu中getter的引用,如果enum不绑定到特定名称,而是绑定到
    Student
    中的字段和stats,则映射也可以用于动态names作为键,枚举作为值。