Java Streams:按两个字段对列表进行分组

Edd*_*Edd 3 java java-stream

我有一份汽车清单

List<Car> cars = ...;

一辆汽车有一个 OwnerOwner有一个ContactNumber

public class Car {
    private Owner owner;
    public Owner getOwner(){
       return owner;
    }
}
Run Code Online (Sandbox Code Playgroud)

所有者

public class Owner {
    private ContactNumber contactNumber;
    public ContactNumber getContactNumber() {
        return contactNumber;
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道我可以使用车主对汽车进行分组

Map<Owner, List<Car>> groupByOwner = cars.stream().collect(groupingBy(Car::getOwner));
Run Code Online (Sandbox Code Playgroud)

有没有办法然后我可以使用流来分组OwnerContactNumber知道一个ContactNumber只能与一个相关联Owner

如果一个 ContactNumber可以由多个Owners共享,?

即创建以下地图:

Map<Owner, List<Car>> groupByOwner = cars.stream().collect(groupingBy(Car::getOwner))
Map<ContactNumber, Map<Owner, List<Car>>> groupByContactNumberAndOwner = groupByOwner...
Run Code Online (Sandbox Code Playgroud)

Fed*_*ner 7

如果你知道

一个ContactNumber只能与一个相关联Owner

然后,您不需要内部映射。只需直接分组ContactNumber

Map<ContactNumber, List<Car>> groupByOwnerContactNumber = cars.stream()
    .collect(Collectors.groupingBy(c -> c.getOwner().getContactNumber()));
Run Code Online (Sandbox Code Playgroud)

不要害怕使用 lambda 表达式;)


编辑:

根据您问题的第二部分:

如果一个ContactNumber可以被多人共享Owners

您可以使用Collectors.groupingBy接受下游收集器的重载版本进行嵌套分组:

Map<ContactNumber, Map<Owner, List<Car>>> groupByOwnerAndContactNumber = 
    cars.stream().collect(Collectors.groupingBy(
            c -> c.getOwner().getContactNumber(),
            Collectors.groupingBy(Car::getOwner)));
Run Code Online (Sandbox Code Playgroud)

另一种方法是按复合键分组(ContactNumber, Owner)。您可以通过让键为 a 来实现这一点List<Object>

Map<List<Object>, List<Car>> groupByCompundOwnerContactNumber = 
    cars.stream().collect(Collectors.groupingBy(
        c -> Arrays.asList(c.getOwner().getContactNumber(), c.getOwner())));
Run Code Online (Sandbox Code Playgroud)

这不需要嵌套分组操作。

注:如果您使用的是Java 9,你可以使用List.of的替代Arrays.asList


Edd*_*Edd 5

为了获得Map<ContactNumber, Map<Owner, List<Car>>>我正在寻找的集合,我必须这样做:

Map<ContactNumber, Map<Owner, List<Car>>> response = cars.stream()
                    .collect(groupingBy(c -> c.getOwner().getContactNumber(), groupingBy(Car::getOwner)));
Run Code Online (Sandbox Code Playgroud)

使用重载收集器

groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
Run Code Online (Sandbox Code Playgroud)

你会在哪里使用:

groupingBy(
    list element -> root key of the map, 
    groupingBy(list element -> second level key of the map)
);
Run Code Online (Sandbox Code Playgroud)