簡體   English   中英

如何使用 Java 8 Streams 分組對象屬性並映射到另一個對象?

[英]How to groupBy object properties and map to another object using Java 8 Streams?

假設我有一組碰碰車,它們的側面有尺寸、顏色和標識符(“汽車代碼”)。

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

現在我需要將碰碰車映射到一個DistGroup對象ListDistGroup對象包含屬性sizecolor和汽車代碼List

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 ),我也認為它可以立即完成(可能使用mappingcollectingAndThenreducing或其他東西),從而產生更優雅的代碼。

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 和構造函數。 您還可以假設equalshashCode方法已正確實現。 另請注意,我使用的是用作分組鍵的SizeColorCombination 這個類顯然包含屬性sizecolor 也可以使用像TuplePairEntry類或任何其他表示兩個任意值組合的類。
編輯:另請注意,當然可以使用 ol' skool for 循環,但這不在此問題的范圍內。

如果我們假設DistGroup具有基於sizecolor hashCode/equals ,您可以這樣做:

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>

解決方案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);
}

進一步將第二個解決方案組合成一個完整的陳述(盡管我自己會很喜歡forEach老實說):

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

可以通過使用收集由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();

查看我的庫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();

我想出了另一個解決方案。

首先,我們稍微調整一下DistGroup類:

class DistGroup {

    private int size;

    private String color;

    private List<String> carCodes;

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

    // So we can use a method reference nicely
    public static DistGroup ofBumperCar(BumperCar car) {
        return new DistGroup(car.size(), car.color(), new ArrayList<>(Arrays.asList(car.carCode())));
    }

    public DistGroup merge(DistGroup other) {
        assert size == other.size;
        assert Objects.equals(color, other.color);

        this.carCodes.addAll(other.carCodes);
        return this;
    }
}

請注意,我刪除了addCarCodes方法,因為它不需要。

現在我們實際上可以使用Collectors::toMap方法很容易地獲得所需的結果。

Collection<DistGroup> dists = cars.stream()
    .collect(Collectors.toMap(
        car -> new SizeColorGroup(car.size(), car.color()),
        DistGroup::ofBumperCar,
        DistGroup::merge
    ))
    .values();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM