代码之家  ›  专栏  ›  技术社区  ›  MC Emperor

如何使用Java 8流分组对象属性和映射到另一个对象?

  •  16
  • MC Emperor  · 技术社区  · 6 年前

    假设我有一组碰碰车,它们的侧面有一个尺寸、一个颜色和一个标识符(“汽车代码”)。

    class BumperCar {
        int size;
        String color;
        String carCode;
    }
    

    现在我需要把保险杠映射到 List 属于 DistGroup 对象,每个对象都包含属性 size , color 和A 汽车代码。

    class DistGroup {
        int size;
        Color color;
        List<String> carCodes;
    
        void addCarCodes(List<String> carCodes) {
            this.carCodes.addAll(carCodes);
        }
    }
    

    例如,

    [
        BumperCar(size=3, color=yellow, carCode=Q4M),
        BumperCar(size=3, color=yellow, carCode=T5A),
        BumperCar(size=3, color=red, carCode=6NR)
    ]
    

    应导致:

    [
        DistGroup(size=3, color=yellow, carCodes=[ Q4M, T5A ]),
        DistGroup(size=3, color=red, carCodes=[ 6NR ])
    ]
    

    我尝试了下面的方法,这实际上是我想要它做的。但问题是,它实现了直接结果(变成 Map )我也认为它可以一次完成(也许使用 mapping collectingAndThen reducing 或者其他原因),从而产生更优雅的代码。

    List<BumperCar> bumperCars = ...
    Map<SizeColorCombination, List<BumperCar>> map = bumperCars.stream()
        .collect(groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())));
    
    List<DistGroup> distGroups = map.entrySet().stream()
        .map(t -> {
            DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
            d.addCarCodes(t.getValue().stream()
                .map(BumperCar::getCarCode)
                .collect(toList()));
            return d;
        })
        .collect(toList());
    

    如何在不使用立即结果变量的情况下获得所需的结果?

    编辑: 如何在不实现直接结果的情况下获得期望的结果?我只不过是在寻找一种方法,这种方法不会实现直接的结果,至少表面上不会。这意味着我不喜欢用这样的东西:

    something.stream()
        .collect(...) // Materializing
        .stream()
        .collect(...); // Materializing second time
    

    当然,如果可能的话。


    注意,为了简洁起见,我省略了getter和constructors。你也可以假设 equals hashCode 方法得到了正确的实现。还要注意,我正在使用 SizeColorCombination 我用它来分组。这个类显然包含属性 大小 颜色 . 类类 Tuple Pair 也可以使用表示两个任意值组合的任何其他类。
    编辑 :还请注意,可以使用ol'skool for循环,而不是当然使用,但这不在本问题的范围内。

    4 回复  |  直到 6 年前
        1
  •  6
  •   Naman    6 年前

    如果我们假设 DistGroup hashCode/equals 基于 size color 你可以这样做:

    bumperCars
        .stream()
        .map(x -> {
            List<String> list = new ArrayList<>();
            list.add(x.getCarCode());
            return new SimpleEntry<>(x, list);
        })
        .map(x -> new DistGroup(x.getKey().getSize(), x.getKey().getColor(), x.getValue()))
        .collect(Collectors.toMap(
            Function.identity(),
            Function.identity(),
            (left, right) -> {
                left.getCarCodes().addAll(right.getCarCodes());
                return left;
            }))
        .values(); // Collection<DistGroup>
    
        2
  •  2
  •   Naman    6 年前

    解决方案1

    将这两个步骤合并为一个步骤:

    List<DistGroup> distGroups = bumperCars.stream()
            .collect(Collectors.groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())))
            .entrySet().stream()
            .map(t -> {
                DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
                d.addCarCodes(t.getValue().stream().map(BumperCar::getCarCode).collect(Collectors.toList()));
                return d;
            })
            .collect(Collectors.toList());
    

    溶液-2

    如果你能用的话,你的中间变量会更好。 groupingBy 两次同时使用属性并将值映射为 List 代码,比如:

    Map<Integer, Map<String, List<String>>> sizeGroupedData = bumperCars.stream()
            .collect(Collectors.groupingBy(BumperCar::getSize,
                    Collectors.groupingBy(BumperCar::getColor,
                            Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))));
    

    简单使用 forEach 要将添加到最终列表,请执行以下操作:

    List<DistGroup> distGroups = new ArrayList<>();
    sizeGroupedData.forEach((size, colorGrouped) ->
            colorGrouped.forEach((color, carCodes) -> distGroups.add(new DistGroup(size, color, carCodes))));
    

    注释 :我已经更新了您的构造函数,以便它接受卡代码列表。

    DistGroup(int size, String color, List<String> carCodes) {
        this.size = size;
        this.color = color;
        addCarCodes(carCodes);
    }
    

    将第二个解决方案进一步组合成一个完整的语句(尽管我自己希望 前额 诚实地说:

    List<DistGroup> distGroups = bumperCars.stream()
            .collect(Collectors.groupingBy(BumperCar::getSize,
                    Collectors.groupingBy(BumperCar::getColor,
                            Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))))
            .entrySet()
            .stream()
            .flatMap(a -> a.getValue().entrySet()
                    .stream().map(b -> new DistGroup(a.getKey(), b.getKey(), b.getValue())))
            .collect(Collectors.toList());
    
        3
  •  0
  •   SEY_91    6 年前

    您可以使用 BiConsumer 那采取 (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc) 作为参数

    Collection<DistGroup> values = bumperCars.stream()
            .collect(HashMap::new, (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc) -> {
                    SizeColorCombination dg = new SizeColorCombination(bc.color, bc.size);
                    DistGroup distGroup = res.get(dg);
                    if(distGroup != null) {
                        distGroup.addCarCode(bc.carCode);
                    }else {
                        List<String> codes = new ArrayList();
                        distGroup = new DistGroup(bc.size, bc.color, codes);
                        res.put(dg, distGroup);
                    }
                    },HashMap::putAll).values();
    
        4
  •  0
  •   123-xyz    6 年前

    查看我的库 AbacusUtil :

    StreamEx.of(bumperCars)
             .groupBy(c -> Tuple.of(c.getSize(), c.getColor()), BumperCar::getCarCode)
             .map(e -> new DistGroup(e.getKey()._1, e.getKey()._2, e.getValue())
             .toList();
    
    推荐文章
    twenty7  ·  按阵列列表分组
    9 年前