Java8流映射分组操作
我有以下两门课:Java8流映射分组操作,java,lambda,java-8,java-stream,Java,Lambda,Java 8,Java Stream,我有以下两门课: 人: public class Person { private final Long id; private final String address; private final String phone; public Person(Long id, String address, String phone) { this.id = id; this.address = address;
人
:
public class Person {
private final Long id;
private final String address;
private final String phone;
public Person(Long id, String address, String phone) {
this.id = id;
this.address = address;
this.phone = phone;
}
public Long getId() {
return id;
}
public String getAddress() {
return address;
}
public String getPhone() {
return phone;
}
@Override
public String toString() {
return "Person [id=" + id + ", address=" + address + ", phone=" + phone + "]";
}
}
import java.util.HashSet;
import java.util.Set;
public class CollectivePerson {
private final Long id;
private final Set<String> addresses;
private final Set<String> phones;
public CollectivePerson(Long id) {
this.id = id;
this.addresses = new HashSet<>();
this.phones = new HashSet<>();
}
public Long getId() {
return id;
}
public Set<String> getAddresses() {
return addresses;
}
public Set<String> getPhones() {
return phones;
}
@Override
public String toString() {
return "CollectivePerson [id=" + id + ", addresses=" + addresses + ", phones=" + phones + "]";
}
}
CollectivePerson
:
public class Person {
private final Long id;
private final String address;
private final String phone;
public Person(Long id, String address, String phone) {
this.id = id;
this.address = address;
this.phone = phone;
}
public Long getId() {
return id;
}
public String getAddress() {
return address;
}
public String getPhone() {
return phone;
}
@Override
public String toString() {
return "Person [id=" + id + ", address=" + address + ", phone=" + phone + "]";
}
}
import java.util.HashSet;
import java.util.Set;
public class CollectivePerson {
private final Long id;
private final Set<String> addresses;
private final Set<String> phones;
public CollectivePerson(Long id) {
this.id = id;
this.addresses = new HashSet<>();
this.phones = new HashSet<>();
}
public Long getId() {
return id;
}
public Set<String> getAddresses() {
return addresses;
}
public Set<String> getPhones() {
return phones;
}
@Override
public String toString() {
return "CollectivePerson [id=" + id + ", addresses=" + addresses + ", phones=" + phones + "]";
}
}
其工作和输出如下:
CollectivePerson [id=1, addresses=[Address 1, Address 4], phones=[Phone 1, Phone 4]]
CollectivePerson [id=2, addresses=[Address 2, Address 6, Address 5], phones=[Phone 5, Phone 2, Phone 6]]
CollectivePerson [id=3, addresses=[Address 3], phones=[Phone 3]]
但我相信有一种更好的方法,分组的流式方法可以达到同样的效果。任何指针都很好。您可以使用带有合并功能的
收集器。toMap
:
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier)
以下是示例输入的输出Map
:
{1=CollectivePerson [id=1, addresses=[Address 1, Address 4], phones=[Phone 1, Phone 4]],
2=CollectivePerson [id=2, addresses=[Address 2, Address 6, Address 5], phones=[Phone 5, Phone 2, Phone 6]],
3=CollectivePerson [id=3, addresses=[Address 3], phones=[Phone 3]]}
使用
groupBy
收集器对您的人员进行分组
List<CollectivePerson> list = persons.stream().collect(Collectors.groupingBy(Person::getId)).entrySet().stream().map(x -> {
// map all the addresses from the list of persons sharing the same id
Set<String> addresses = x.getValue().stream().map(Person::getAddress).collect(Collectors.toSet());
// map all the phones from the list of persons sharing the same id
Set<String> phones = x.getValue().stream().map(Person::getPhone).collect(Collectors.toSet());
// declare this constructor that takes three parameters
return new CollectivePerson(x.getKey(), addresses, phones);
}).collect(Collectors.toList());
List List=persons.stream().collect(Collectors.groupingBy(Person::getId)).entrySet().stream().map(x->{
//映射共享相同id的人员列表中的所有地址
Set addresses=x.getValue().stream().map(Person::getAddress).collect(Collectors.toSet());
//从共享相同id的人员列表中映射所有电话
Set phones=x.getValue().stream().map(Person::getPhone).collect(Collectors.toSet());
//声明这个接受三个参数的构造函数
返回新的CollectivePerson(x.getKey()、地址、电话);
}).collect(Collectors.toList());
要使其工作,您需要添加此构造函数:
public CollectivePerson(Long id, Set<String> addresses, Set<String> phones) {
this.id = id;
this.addresses = addresses;
this.phones = phones;
}
public CollectivePerson(长id、设置地址、设置电话){
this.id=id;
this.address=地址;
这个。电话=电话;
}
您应该使用收集器,而不是操作外部映射。有toMap
和groupingBy
,它们都可以解决这个问题,尽管由于您的类设计有点冗长。主要障碍是缺乏一种现有的方法来将人
合并为集合人
或从给定的人
实例构造集合人
,或合并两个集合人
实例的方法
使用内置收集器的一种方法是
List<CollectivePerson> collectivePersons = persons.stream()
.map(p -> {
CollectivePerson cp = new CollectivePerson(p.getId());
cp.getAddresses().add(p.getAddress());
cp.getPhones().add(p.getPhone());
return cp;
})
.collect(Collectors.collectingAndThen(Collectors.toMap(
CollectivePerson::getId, Function.identity(),
(cp1, cp2) -> {
cp1.getAddresses().addAll(cp2.getAddresses());
cp1.getPhones().addAll(cp2.getPhones());
return cp1;
}),
m -> new ArrayList<>(m.values())
));
List collectivePersons=persons.stream()
.map(p->{
CollectivePerson cp=新的CollectivePerson(p.getId());
cp.getAddresses().add(p.getAddress());
cp.getPhone().add(p.getPhone());
返回cp;
})
.collect(收集器.collecting然后(收集器.toMap(
CollectivePerson::getId,Function.identity(),
(cp1,cp2)->{
cp1.getAddresses().addAll(cp2.getAddresses());
cp1.getPhones().addAll(cp2.getPhones());
返回cp1;
}),
m->new ArrayList(m.values())
));
但在这种情况下,自定义收集器可能更简单:
Collection<CollectivePerson> collectivePersons = persons.stream()
.collect(
HashMap<Long,CollectivePerson>::new,
(m,p) -> {
CollectivePerson cp=m.computeIfAbsent(p.getId(), CollectivePerson::new);
cp.getAddresses().add(p.getAddress());
cp.getPhones().add(p.getPhone());
},
(m1,m2) -> m2.forEach((l,cp) -> m1.merge(l, cp, (cp1,cp2) -> {
cp1.getAddresses().addAll(cp2.getAddresses());
cp1.getPhones().addAll(cp2.getPhones());
return cp1;
}))).values();
collectionpersons=persons.stream()
.收集(
HashMap::新建,
(m,p)->{
CollectivePerson cp=m.ComputeFabSent(p.getId(),CollectivePerson::new);
cp.getAddresses().add(p.getAddress());
cp.getPhone().add(p.getPhone());
},
(m1,m2)->m2.forEach((l,cp)->m1.merge(l,cp,(cp1,cp2)->{
cp1.getAddresses().addAll(cp2.getAddresses());
cp1.getPhones().addAll(cp2.getPhones());
返回cp1;
}))).values();
两者都将受益于一个预定义的方法来合并两个CollectivePerson
实例,而第一个变体也将受益于CollectivePerson(长id、设置地址、设置电话)
构造函数,或者更好的是,受益于CollectivePerson(Person p)
构造函数,而第二个构造函数将受益于CollectivePerson.add(Person p)
方法
请注意,第二个变量返回映射
s值的集合
视图,无需复制。如果您确实需要一个列表
,您可以像使用finisher函数中的第一个变量一样,使用新的ArrayList(«map».values())
一样简单地压缩它。无需指定HashMap::new
;此任务不要求映射是HashMap
…@Holger的实例,您是对的。出于某种原因,我认为采用合并功能的toMap
的唯一变体也需要供应商。也就是说,我刚刚注意到3参数toMap(即没有供应商)的实现是returntomap(keyMapper、valueMapper、mergeFunction、HashMap::new)代码>:)是的,在当前的实现中,它总是生成一个HashMap
,就像toList()
总是生成一个ArrayList
,但是,这并不能保证,如果不需要获得这些类型的确切实例,您应该允许实现更改,以获得更改承诺的任何好处。反过来说,如果不需要合并功能,没有任何方法可以选择地图供应商。谢谢你,我已经选择了你的第二个版本,它非常快。在约10秒内对约10000000人进行操作。
Map<Long, CollectivePerson> map = persons.stream().
collect(Collectors.groupingBy(Person::getId,
Collectors.collectingAndThen(Collectors.toList(),
Main::downColl)));
Map<Long, CollectivePerson> map = persons.stream().
collect(Collectors.groupingBy(Person::getId,
Collectors.collectingAndThen(Collectors.toList(),
Main::downColl)));
public static CollectivePerson downColl(List<Person> ps) {
CollectivePerson cp = new CollectivePerson(ps.get(0).getId());
for (Person p:ps) {
cp.getAddresses().add(p.getAddress());
cp.getPhones().add(p.getPhone());
}
return cp;
}