在Java 8中创建嵌套的父子列表

use*_*550 9 java iteration list java-8 java-stream

我是Java 8的新手,需要解决以下问题.

我有两个课程如下:

class Person {
    String name;
    int age;
    List<Address> address;
}

class Address {
    String street;
    String city;
    String country;
}
Run Code Online (Sandbox Code Playgroud)

现在我有一个来自数据库的列表,如下所示:

List<Person> findPerson;

adam
26
<123, xyz, yyy>

adam
26
<456, rrr, kkk>

bill
31
<666, uuu, hhh>
Run Code Online (Sandbox Code Playgroud)

现在我需要将相同的人物对象与不同的地址对象组合在一起,如下所示?

List<Person> findPerson;

adam
26
<123, xyz, 456>
<456, rrr, 123>

bill
31
<666, uuu, 999>
Run Code Online (Sandbox Code Playgroud)

如何在Java 8流中完成此操作?

Ous*_* D. 9

我建议你实现equalshashcode在你的Person类.

例:

@Override
public boolean equals(Object o) {
       if (this == o) return true;
       if (o == null || getClass() != o.getClass()) return false;

       Person person = (Person) o;

       if (age != person.age) return false;
       return name != null ? name.equals(person.name) : person.name == null;
}

@Override
public int hashCode() {
       int result = name != null ? name.hashCode() : 0;
       result = 31 * result + age;
       return result;
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以将a Map<Person, List<Address>>作为结果集的接收器类型.

Map<Person, List<Address>> resultSet = findPerson.stream()
    .collect(
        Collectors.groupingBy(
            Function.identity(),
            Collectors.flatMapping(
                p -> p.getAddress().stream(), 
                Collectors.toList()
            )
        )
    )
;
Run Code Online (Sandbox Code Playgroud)

该解决方案使用flatMapping仅从Java-9开始提供的收集器.

如果您还在使用Java-8,那么您可以:

Map<Person, List<Address>> resultSet = findPerson.stream()
    .collect(
        Collectors.groupingBy(
            Function.identity(),
            Collectors.collectingAndThen(
                Collectors.mapping(
                    Person::getAddress, 
                    Collectors.toList()
                ),
                lists -> lists.stream()
                    .flatMap(List::stream)
                    .collect( Collectors.toList() )
            )
        )
    )   
;
Run Code Online (Sandbox Code Playgroud)

注意 -这是,我目前假设两个或两个以上的人是基于认为是平等的nameage,但是,如果它只是基于name或只是age那么你需要tweek的equals/ hashcode方法一点点.


Fed*_*ner 5

假设Person工具hashCodeequals持之以恒,你能收集到Map:

Map<Person, Person> map = findPerson.stream()
    .collect(Collectors.toMap(
        p -> p,
        p -> p,
        (oldPerson, newPerson) -> { 
            oldPerson.getAddress().addAll(newPerson.getAddress()); 
            return oldPerson; 
        }));
Run Code Online (Sandbox Code Playgroud)

这使用Collectors.toMap,通过新人与已经在地图中的旧人合并,并且通过合并我们的意思是添加地址.

现在,在地图的值中,您有合并的人:

Collection<Person> result = map.values();
Run Code Online (Sandbox Code Playgroud)

如果您需要返回一个列表:

List<Person> result = new ArrayList<>(map.values());
Run Code Online (Sandbox Code Playgroud)

编辑:

这假设List<Address>每个人的可变性(这样可行addAll).情况并非总是如此,特别是如果您不想破坏封装,则不应该Person从外部修改对象的地址.

在这种情况下,您可以提供一种方法,Person在不破坏封装的情况下合并地址:

public Person merge(Person another) {
    this.address.addAll(another.address);
    return this;
}
Run Code Online (Sandbox Code Playgroud)

或者,如果地址列表是不可变的:

public Person merge(Person another) {
    List<Address> merged = new ArrayList<>(this.address);
    merged.addAll(another.address);
    this.address = merged;
    return this;
}
Run Code Online (Sandbox Code Playgroud)

现在收集器中的代码可以重构为:

Map<Person, Person> map = findPerson.stream()
    .collect(Collectors.toMap(
        p -> p, 
        p -> p,
        Person::merge));
Run Code Online (Sandbox Code Playgroud)


tso*_*akp 2

另一种方法不需要重写equalsand hashCode

List<Person> merged = findPerson.stream()
    .collect( 
        Collectors.collectingAndThen(
            Collectors.toMap( 
                (p) -> new AbstractMap.SimpleEntry<>( p.getName(), p.getAge() ), 
                Function.identity(), 
                (left, right) -> { 
                    left.getAddress().addAll( right.getAddress() ); 
                    return left; 
                }
            ),        
            ps -> new ArrayList<>( ps.values() ) 
        ) 
    )
;
Run Code Online (Sandbox Code Playgroud)