Java 用流访问每个流中的元素替换增强的 for 循环

Java replace enhanced for loop with stream accessing elements from each stream

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

我有一些包含嵌套列表的对象。我通常会使用嵌套的 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);
                }
            }
        }

你可以这样做:

    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());

您将不得不使用

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

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

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)