猿问

如何使用 Java 8 Streams 对对象属性进行分组并映射到另一个对象?

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


class BumperCar {

    int size;

    String color;

    String carCode;

}

现在我需要将碰碰车映射到一个List对象DistGroup,每个对象都包含属性和size汽车代码。colorList


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

]

我尝试了以下方法,它实际上做了我想做的事情。但问题是它实现了中间结果(到 a 中Map),我也认为它可以立即完成(可能使用mappingorcollectingAndThen或reducingor ),从而产生更优雅的代码。


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 和构造函数。您还可以假设equals和hashCode方法已正确实现。另请注意,我使用的SizeColorCombination是我用作分组键的。这个类显然包含属性size和color。也可以使用诸如Tuple、之类的Pair类或表示两个任意值的组合的任何其他类。编辑:还请注意,当然可以使用 ol' skool for 循环,但这不在此问题的范围内。Entry


慕森王
浏览 421回答 3
3回答

墨色风雨

如果我们假设它DistGroup基于hashCode/equalsand size,color你可以这样做:bumperCars&nbsp; &nbsp; .stream()&nbsp; &nbsp; .map(x -> {&nbsp; &nbsp; &nbsp; &nbsp; List<String> list = new ArrayList<>();&nbsp; &nbsp; &nbsp; &nbsp; list.add(x.getCarCode());&nbsp; &nbsp; &nbsp; &nbsp; return new SimpleEntry<>(x, list);&nbsp; &nbsp; })&nbsp; &nbsp; .map(x -> new DistGroup(x.getKey().getSize(), x.getKey().getColor(), x.getValue()))&nbsp; &nbsp; .collect(Collectors.toMap(&nbsp; &nbsp; &nbsp; &nbsp; Function.identity(),&nbsp; &nbsp; &nbsp; &nbsp; Function.identity(),&nbsp; &nbsp; &nbsp; &nbsp; (left, right) -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; left.getCarCodes().addAll(right.getCarCodes());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return left;&nbsp; &nbsp; &nbsp; &nbsp; }))&nbsp; &nbsp; .values(); // Collection<DistGroup>

萧十郎

解决方案-1只需将两个步骤合并为一个:List<DistGroup> distGroups = bumperCars.stream()&nbsp; &nbsp; &nbsp; &nbsp; .collect(Collectors.groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())))&nbsp; &nbsp; &nbsp; &nbsp; .entrySet().stream()&nbsp; &nbsp; &nbsp; &nbsp; .map(t -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; d.addCarCodes(t.getValue().stream().map(BumperCar::getCarCode).collect(Collectors.toList()));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return d;&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; .collect(Collectors.toList());解决方案-2groupingBy如果您可以使用属性两次并将值映射为List代码,那么您的中间变量会好得多,例如:Map<Integer, Map<String, List<String>>> sizeGroupedData = bumperCars.stream()&nbsp; &nbsp; &nbsp; &nbsp; .collect(Collectors.groupingBy(BumperCar::getSize,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Collectors.groupingBy(BumperCar::getColor,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))));并简单地使用forEach添加到最终列表中:List<DistGroup> distGroups = new ArrayList<>();sizeGroupedData.forEach((size, colorGrouped) ->&nbsp; &nbsp; &nbsp; &nbsp; colorGrouped.forEach((color, carCodes) -> distGroups.add(new DistGroup(size, color, carCodes))));注意:我已经更新了您的构造函数,使其接受卡代码列表。DistGroup(int size, String color, List<String> carCodes) {&nbsp; &nbsp; this.size = size;&nbsp; &nbsp; this.color = color;&nbsp; &nbsp; addCarCodes(carCodes);}进一步将第二个解决方案组合成一个完整的陈述(尽管我自己喜欢forEach老实说):List<DistGroup> distGroups = bumperCars.stream()&nbsp; &nbsp; &nbsp; &nbsp; .collect(Collectors.groupingBy(BumperCar::getSize,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Collectors.groupingBy(BumperCar::getColor,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))))&nbsp; &nbsp; &nbsp; &nbsp; .entrySet()&nbsp; &nbsp; &nbsp; &nbsp; .stream()&nbsp; &nbsp; &nbsp; &nbsp; .flatMap(a -> a.getValue().entrySet()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .stream().map(b -> new DistGroup(a.getKey(), b.getKey(), b.getValue())))&nbsp; &nbsp; &nbsp; &nbsp; .collect(Collectors.toList());

大话西游666

查看我的图书馆AbacusUtil:StreamEx.of(bumperCars) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.groupBy(c&nbsp;->&nbsp;Tuple.of(c.getSize(),&nbsp;c.getColor()),&nbsp;BumperCar::getCarCode) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(e&nbsp;->&nbsp;new&nbsp;DistGroup(e.getKey()._1,&nbsp;e.getKey()._2,&nbsp;e.getValue()) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.toList();
随时随地看视频慕课网APP

相关分类

Java
我要回答