按对象属性将地图分组到新地图 [英] Grouping a map by object's property to a new Map

查看:92
本文介绍了按对象属性将地图分组到新地图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,让我添加实际的示例代码":

First things first, let me add the actual "example code":

Map<CarBrand, List<Car>> allCarsAndBrands = new HashMap();

final String bmwBrandName = "BMW";
final String audiBrandName = "AUDI";

List<Car> bmwCars = new ArrayList();
bmwCars.add(new Car(CarType.FAST, "Z4", "silver", bmwBrandName));
bmwCars.add(new Car(CarType.FAST, "M3", "red", bmwBrandName));
bmwCars.add(new Car(CarType.SLOW, "X1", "black", bmwBrandName));

List<Car> audiCars = new ArrayList();
audiCars.add(new Car(CarType.FAST, "S3", "yellow", audiBrandName));
audiCars.add(new Car(CarType.FAST, "R8", "silver", audiBrandName));
audiCars.add(new Car(CarType.SLOW, "A1", "white", audiBrandName));

allCarsAndBrands.put(new CarBrand(bmwBrandName), bmwCars);
allCarsAndBrands.put(new CarBrand(audiBrandName), audiCars);

Map<CarType, Map<CarBrand, List<Car>>> mappedCars;

问题

我对此的目标是用CarType填充mappedCars,这将导致两个大集合:一个集合包含所有FAST汽车,另一个集合所有SLOW汽车(或任何未来的类型",每个都有) CarBrand和相关汽车的先前地图结构.

My goal on this is to populate mappedCars by CarType, which would result in two big sets: one containing all FAST cars and the other all SLOW cars (or any future "types", each one having the previous map structure with CarBrand and the related cars).

我目前无法为此映射其他列表中的列表的映射"找到对集合/流的正确使用.我在其他情况下使用简单的地图/列表,但事实证明这对我来说比较棘手.

I'm currently failing to find the proper use of Collections/Streams for this "map with lists inside other map". I've had other cases with simple maps/lists but this one is proving to be trickier for me.

尝试

这是初始代码尝试":

mappedCars = allCarsAndBrands.entrySet()
                             .stream()
                             .collect(
                               groupingBy(Car::getType, 
                                 groupingBy(Map.Entry::getKey)
                               )
                             );

我也收到了非静态无法引用错误"消息. (Map.Entry :: getKey),但这是由于我未能匹配实际的预期收益(

I'm also getting the "non-static cannot be referenced error" (Map.Entry::getKey) but this is due the fact that I'm failing to match the actual expected return (Static context cannot access non-static in Collectors)

这时我很困惑,也尝试使用Collectors.toMap,但仍然无法得到有效的分组.

I'm simply confused at this point, tried using Collectors.toMap too but still can't get a working grouping.

其他

这是此示例的类定义:

class CarBrand {
   CarBrand(String name) {
      this.name = name;
   }
   String name;
}

class Car {
    Car(CarType type, String name, String color, String brandName) {
        this.type = type;
        this.name = name;
        this.color = color;
        this.brandName = brandName;
    }

    public CarType getType() {
        return type;
    }

    CarType type;
    String name;
    String color;
    String brandName;
}

enum CarType {
   FAST,
   SLOW,
}

"DIRTY"解决方案

这是一个骇客"解决方案(根据评论建议,将检查答案!):

Here's a "hackish" solution (based on the comments suggestions, will check the answers!):

Map<CarType, Map<CarBrand, List<Car>>> mappedCars = allCarsAndBrands
                .values()
                .stream()
                .flatMap(List::stream)
                .collect(Collectors.groupingBy(
                        Car::getType,
                        Collectors.groupingBy(
                                car -> allCarsAndBrands.keySet().stream().filter(brand -> brand.name == car.brandName).findFirst().get(),
                                Collectors.toList()
                        )
                ));

如评论中所述(应该在此之前添加),存在业务约束".这增加了解决方案的一些限制.我也不想创建一个新的CarBrand,因为在现实世界中,这并不像在此上看到的那么简单...但是,再次使用原始地图并进行过滤+查找是很糟糕的.

As mentioned in the comments (should've added here before), there's a "business constraint" that adds some limitations for the solution. I also didn't feel like creating a new CarBrand since in the real world that's not that simple as seen on this... but again, using the original map and filtering + find is just bad.

推荐答案

使用现有模型以及嵌套分组的初始方法,您正在朝正确的方向思考.可以考虑在迭代条目时变平Map的值部分.

With the use of existing models, and the initial approach of nested grouping you were thinking in the right direction. The improvement could be made in thinking about flattening the value part of the Map while iterating over the entries.

allCarsAndBrands.entrySet().stream()
        .flatMap(e -> e.getValue().stream()
                .map(car -> new AbstractMap.SimpleEntry<>(e.getKey(), car)))

一旦有了,分组概念的工作原理几乎相同,但是现在默认的返回分组值将是条目类型.因此,进一步需要mapping.这使整体解决方案类似于:

Once you have that, the grouping concept works pretty much the same, but now the default returned grouped values would instead be of the entry type. Hence a mapping is further required. This leaves the overall solution to be something like :

Map<CarType, Map<CarBrand, List<Car>>> mappedCars =
        allCarsAndBrands.entrySet().stream()
                .flatMap(e -> e.getValue().stream()
                        .map(car -> new AbstractMap.SimpleEntry<>(e.getKey(), car)))
                .collect(Collectors.groupingBy(e -> e.getValue().getType(),
                        Collectors.groupingBy(Map.Entry::getKey,
                                Collectors.mapping(Map.Entry::getValue,
                                        Collectors.toList()))));

这篇关于按对象属性将地图分组到新地图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆