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流中完成此操作?
我建议你实现equals并hashcode在你的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)
注意 -这是,我目前假设两个或两个以上的人是基于认为是平等的name和 age,但是,如果它只是基于name或只是age那么你需要tweek的equals/ hashcode方法一点点.
假设Person工具hashCode和equals持之以恒,你能收集到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)
另一种方法不需要重写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)