如何将具有列表中相同值的两个字段的对象组合到一个简化列表中?

时间:2017-09-27 15:37:50

标签: java java-8

我有这个类的对象列表:

class Item
{
    private String name;
    private String parentName;
    private Integer someValue;
    private Double anotherValue;

    public Item() {}

    //...elided getters/setters...
}

我列出了以下值:

//PSEUDO CODE (Not JavaScript, but using JSON is easier to follow)
List<Item> items = [
    {
        "name": "Joe",
        "parentName": "Frank",
        "someValue": 10,
        "anotherValue": 15.0
    },
    {
        "name": "Joe",
        "parentName": "Frank",
        "someValue": 40,
        "anotherValue": 0.5
    },
    {
        "name": "Joe",
        "parentName": "Jack",
        "someValue": 10,
        "anotherValue": 10.0
    },
    {
        "name": "Jeff",
        "parentName": "Frank",
        "someValue": 10,
        "anotherValue": 10.0
    }
];

我希望将其合并到此列表中:

List<Item> items = [
    {
        "name": "Joe",
        "parentName": "Frank",
        "someValue": 50,
        "anotherValue": 15.5
    },
    {
        "name": "Joe",
        "parentName": "Jack",
        "someValue": 10,
        "anotherValue": 10.0
    },
    {
        "name": "Jeff",
        "parentName": "Frank",
        "someValue": 10,
        "anotherValue": 10.0
    }
];

基本上规则是:

  • 如果两个对象中的name和parentName相同:
    • 将它们合并为一个对象
    • 总结“someValue”字段
    • 求和“anotherValue”字段
  • 如果name或parentName之一是diff,请不要合并

我将如何在Java 8流中执行此操作?

我开始将它们放入桶中(见下文),但我不确定从哪里开始:

items
    .stream()
    .collect(
        Collectors.groupingBy(
            item -> new ArrayList<String>( Arrays.asList( item.getName(), item.getParentName() ) ) 
        )
    );

2 个答案:

答案 0 :(得分:5)

collect(groupingBy())方法返回一张地图。

如果您定义以下类:

class Tuple {
    String name;
    String parentName;
}

然后你可以获得以下地图:

Map<Tuple, List<Item>> groupedItems = items.stream().collect(
            groupingBy(item -> new Tuple(item.getName(), item.getParentName())));

现在你可以这样操作:

List<Item> finalItems = groupedItems.entrySet().stream().map(entry -> 
               new Item(entry.getKey().name, 
                        entry.getKey().parentName,
                        entry.getValue().stream().mapToInt(
                                  item -> item.someValue).sum(),
                        entry.getValue().stream().mapToDouble(
                                  item -> item.anotherValue).sum()))
               .collect(Collectors.toList());

答案 1 :(得分:1)

这是一个使用一些Java8结构的解决方案(尽管它本质上是通过List的循环,暂时将项目存储在Map中)。 :

List<Item> items;
Map<String, Item> masterItems;

    items.forEach(item -> {
        if (masterItems.containsKey(item.getName() + item.getParent())) {
            Item matchedItem = masterItems.get(item.getName() + item.getParent());
            matchedItem.setSomeValue(matchedItem.getSomeValue() + item.getSomeValue());
            matchedItem.setOtherValue(matchedItem.getOtherValue() + item.getOtherValue());
        } else {
            masterItems.put(item.getName() + item.getParent(), item);
        }
    });
    items.clear();
    items.addAll(masterItems.values());

使用更多Java8构造可能会被改进......我会去看看我能做什么