【问题标题】:Java replace enhanced for loop with stream accessing elements from each streamJava用流访问每个流中的元素替换增强的for循环
【发布时间】:2022-01-10 00:52:07
【问题描述】:

第一次对我这么简单的问题..

我有一些包含嵌套列表的对象。我通常会使用嵌套的 for 循环来对这些进行任何转换,但我热衷于探索 Java8 Streams。本质上,我正在尝试创建一个输出对象,该对象将由在每个嵌套列表中访问的字段组成。

我在下面分享了一个非常简单的示例,以及我通常如何使用增强的 for 循环来执行此操作。任何人都可以与我分享我应该如何做这将流?另外,如果我们假设这些列表中的一些将具有 [0:M] 的基数,即它们是可选列表,我将如何使流为空安全? 谢谢

    // Objects below
    class Qualification {
        String qualificationName;
        String qualificationValue;
    }

    class Person {
        String name;
        List<Qualification> qualifications;
    }

class Group {
        String groupId;
        List<Person> people;
    }

    class Output {
        String groupId;
        String name;
        String qualificationName;
        String qualificationValue;
    }

以下是我通常会如何执行此转换。

// Create empty list to hold the output objects    
List<Output> outputList = new ArrayList<Output>();
        for(Group g : groups) {

            for (Person p : g.getPeople()) {

                for (Qualification q : p.getQualifications()) {
                   // Compose new object and add to list

                    Output output = new Output();
                    output.setId(g.getId());
                    output.setName(p.getName());
                    output.setQualificationValue(q.getQualificationValue());
                    output.setQualificationName(q.getQualificationName());
                            
                    outputList.add(output);
                }
            }
        }

【问题讨论】:

    标签: java foreach java-8 stream


    【解决方案1】:

    你可以这样做:

        List<Output> outputList = groups.stream()
                .flatMap(g -> g.getPeople().stream()
                        .flatMap(p ->
                                p.getQualifications()
                                        .stream()
                                        .map(q -> {
                                            Output output = new Output();
                                            output.setGroupId(g.getGroupId());
                                            output.setName(p.getName());
                                            output.setQualificationValue(q.getQualificationValue());
                                            output.setQualificationName(q.getQualificationName());
                                            return output;
                                        })
                        ))
                .collect(Collectors.toList());
    

    【讨论】:

      【解决方案2】:

      你必须使用

      • filter() 处理空列表,并且
      • flatMap() 从内部列表创建子流

      (我使用了 Lombok 注释;您可以使用常规的 getter/setter 和构造函数。)

      public class NestedForLoopsIntoStreams{
          @NoArgsConstructor @AllArgsConstructor( staticName = "of" )
          @Getter @Setter
          private static class Qualification {
              String qualificationName;
              String qualificationValue;
          }
      
          @NoArgsConstructor @AllArgsConstructor( staticName = "of" )
          @Getter @Setter
          private static class Person {
              String name;
              List<Qualification> qualifications;
          }
      
          @NoArgsConstructor @AllArgsConstructor( staticName = "of" )
          @Getter @Setter
          private static class Group {
              String groupId;
              List<Person> people;
          }
      
          @NoArgsConstructor @AllArgsConstructor( staticName = "of" )
          @Getter @Setter @ToString
          private static class Output {
              String groupId;
              String name;
              String qualificationName;
              String qualificationValue;
          }
          
          public static void main( String[] args ){
              List<Group> groups = testData();
              List<Output> outputs = 
              groups.stream().filter( g -> g.getPeople() != null )
                  .flatMap( g -> {
                      return g.getPeople().stream().filter( p -> p.getQualifications() != null )
                          .flatMap( p -> {
                              return p.getQualifications().stream().map( q ->
                                  Output.of( g.groupId, p.name, q.qualificationName, q.qualificationValue )
                              );
                          }
                  );
              } ).collect( Collectors.toList() );
              
              outputs.forEach( System.out::println );
          }
      
          private static List<Group> testData(){
              return Arrays.asList(
                  Group.of( "1", Arrays.asList( 
                      Person.of( "Person1", Arrays.asList( Qualification.of( "Engg", "BTech" ) ) ),
                      Person.of( "Person2", Arrays.asList( Qualification.of( "Engg", "MTech" ) ) )
                  ) ),
                  Group.of( "2", Arrays.asList( 
                      Person.of( "Person3", Arrays.asList( Qualification.of( "Engg", "BTech" ) ) )
                  ) ),
                  /* Person with no qualication */
                  Group.of( "3", Arrays.asList(
                      Person.of( "Person4", Arrays.asList( Qualification.of( "Engg", "BTech" ) ) ),
                      Person.of( "Person5", null )
                  ) ),
                  /* No people */
                  Group.of( "4", null ) 
              );
          }
      }
      

      运行它会给出这个输出:

      NestedForLoopsIntoStreams.Output(groupId=1, name=Person1, qualificationName=Engg, qualificationValue=BTech)
      NestedForLoopsIntoStreams.Output(groupId=1, name=Person2, qualificationName=Engg, qualificationValue=MTech)
      NestedForLoopsIntoStreams.Output(groupId=2, name=Person3, qualificationName=Engg, qualificationValue=BTech)
      NestedForLoopsIntoStreams.Output(groupId=3, name=Person4, qualificationName=Engg, qualificationValue=BTech)
      

      【讨论】:

      • 谢谢,这适用于我使用的示例!如果我可以要求跟进;在流嵌套在对象中的场景中。假设 Person 有两个类,Personal(带有个人信息)和 Academic。在 Academic 中,我将拥有 List 资格。所以本质上,我是通过流进行平面映射,但最终流需要通过 p.getAcademic().getQualifications().stream() 访问。那会是一个映射函数吗?并使其为空安全?谢谢!
      • 这将是完全相同的一组 filter()s 和 flatMap()s,只是你必须在你的资格过滤器中包含检查。它会变成这样:filter( p -&gt; p.getAcademics() != null &amp;&amp; p.getAcademics().getQualifications() != null )。同样,您必须在调用 p.getPersonal().getName() 之前对 p.getPersonal() 进行空值检查。
      猜你喜欢
      • 1970-01-01
      • 2020-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      • 2023-04-04
      相关资源
      最近更新 更多