Java流:通过两个字段对列表进行分组

3

我有一份汽车清单

List<Car> cars = ...;

一辆汽车拥有一个所有者,而这个所有者又有一个联系电话号码

汽车

public class Car {
    private Owner owner;
    public Owner getOwner(){
       return owner;
    }
}

所有者

public class Owner {
    private ContactNumber contactNumber;
    public ContactNumber getContactNumber() {
        return contactNumber;
    }
}

我知道可以使用以下方法按所有者分组汽车:

Map<Owner, List<Car>> groupByOwner = cars.stream().collect(groupingBy(Car::getOwner));

如果我知道一个 ContactNumber 只能关联一个 Owner,那么有没有办法使用流按 OwnerContactNumber 进行分组呢?

如果一个 ContactNumber 可以由多个 Owner 共享,我该如何做到这一点呢?

即创建下面的映射:

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

如果ContactNumber是每个所有者唯一的,那么为什么在groupByContactNumberAndOwner中要有ContactNumber所有者的Map - tsolakp
可能是Java 8中按多个字段名称分组的重复问题。 - sknt
@tsolakp 你是对的,如果没有限制,即如果一个联系电话可以由多个所有者共享,我该怎么做呢? - Eduardo
2个回答

8

如果您知道

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

那么,您不需要使用内部映射。直接按 ContactNumber 进行分组:

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

不要害怕使用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)));

另一种方法是通过组合键(联系号码,拥有者)进行分组。您可以通过让键为List<Object>来实现:

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

这不需要嵌套分组操作。

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


谢谢。如果一个联系号码可以由多个所有者共享,我应该如何区别处理呢? - Eduardo

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

使用重载的收集器

groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)

您会在哪些情况下使用:

groupingBy(
    list element -> root key of the map, 
    groupingBy(list element -> second level key of the map)
);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接