Java Stream GroupBy 和Reduce

hak*_*ata 5 java reducing java-stream collectors groupingby

我有一个 Item 类,其中包含代码、数量和金额字段,以及可能包含许多项目(具有相同代码)的项目列表。我想按代码对项目进行分组并汇总它们的数量和金额。

我能够使用流groupingBy和实现一半的目标reduce。分组有效,但减少是将所有分组的项目减少为一个在不同代码(groupingBy键)上重复的单个项目。

不应该在此处减少减少地图中每个代码的项目列表吗?为什么它要为所有人重新调整相同的组合项目。

下面是示例代码。

import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.Map;

class HelloWorld {
    public static void main(String[] args) {
        List<Item> itemList = Arrays.asList(
            createItem("CODE1", 1, 12),
            createItem("CODE2", 4, 22),
            createItem("CODE3", 5, 50),
            createItem("CODE4", 2, 11),
            createItem("CODE4", 8, 20),
            createItem("CODE2", 1, 42)
        );
        
        Map<String, Item> aggregatedItems = itemList
            .stream()
            .collect(Collectors.groupingBy(
                Item::getCode,
                Collectors.reducing(new Item(), (aggregatedItem, item) -> {
                    int aggregatedQuantity = aggregatedItem.getQuantity();
                    double aggregatedAmount = aggregatedItem.getAmount();
                    
                    aggregatedItem.setQuantity(aggregatedQuantity + item.getQuantity());
                    aggregatedItem.setAmount(aggregatedAmount + item.getAmount());
                    
                    return aggregatedItem;
                })
            ));
        
        System.out.println("Map total size: " + aggregatedItems.size()); // expected 4
        System.out.println();
        aggregatedItems.forEach((key, value) -> {
            System.out.println("key: " + key);
            System.out.println("value - quantity: " + value.getQuantity() + " - amount: " + value.getAmount());
            System.out.println();
        });
    }
    
    private static Item createItem(String code, int quantity, double amount) {
        Item item = new Item();
        item.setCode(code);
        item.setQuantity(quantity);
        item.setAmount(amount);
        return item;
    }
}

class Item {
    private String code;
    private int quantity;
    private double amount;
    
    public Item() {
        quantity = 0;
        amount = 0.0;
    }
    
    public String getCode() { return code; }
    public int getQuantity() { return quantity; }
    public double getAmount() { return amount; }
    
    public void setCode(String code) { this.code = code; }
    public void setQuantity(int quantity) { this.quantity = quantity; }
    public void setAmount(double amount) { this.amount = amount; }
}
Run Code Online (Sandbox Code Playgroud)

下面是输出。

Map total size: 4

key: CODE2
value - quantity: 21 - amount: 157.0

key: CODE1
value - quantity: 21 - amount: 157.0

key: CODE4
value - quantity: 21 - amount: 157.0

key: CODE3
value - quantity: 21 - amount: 157.0
Run Code Online (Sandbox Code Playgroud)

kni*_*ttl 1

您不得修改的输入参数Collectors.reducingnew Item()仅执行一次,所有归约操作将共享相同的“聚合实例”。换句话说:地图将包含相同的值实例 4 次(您可以使用System.identityHashCode()或 通过比较引用相等性来轻松检查自己aggregatedItems.get("CODE1") == aggregatedItems.get("CODE2"):)。

相反,返回一个新的结果实例:

        final Map<String, Item> aggregatedItems = itemList
            .stream()
            .collect(Collectors.groupingBy(
                Item::getCode,
                Collectors.reducing(new Item(), (item1, item2) -> {
                    final Item reduced = new Item();
                    reduced.setQuantity(item1.getQuantity() + item2.getQuantity());
                    reduced.setAmount(item1.getAmount() + item2.getAmount());
                    return reduced;
                })
            ));
Run Code Online (Sandbox Code Playgroud)

输出:

Map total size: 4

key: CODE2
value - quantity: 5 - amount: 64.0

key: CODE1
value - quantity: 1 - amount: 12.0

key: CODE4
value - quantity: 10 - amount: 31.0

key: CODE3
value - quantity: 5 - amount: 50.0
Run Code Online (Sandbox Code Playgroud)