根据流中的另一个字段汇总字段



我有一个对象列表,看起来像这样:

{
    value=500
    category="GROCERY"
},
{
    value=300
    category="GROCERY"
},
{
    value=100
    category="FUEL"
},
{
    value=300
    category="SMALL APPLIANCE REPAIR"
},
{
    value=200
    category="FUEL"
}

我想把它转换成一个对象列表,看起来像这样:

{
    value=800
    category="GROCERY"
},
{
    value=300
    category="FUEL"
},
{
    value=300
    category="SMALL APPLIANCE REPAIR"
}

基本上是将同一类别的所有值相加。

我应该使用flatMap吗?减少?我不明白其中的细微差别。

帮助吗?

编辑:

这个问题有很多相似之处:在流Java 8 api中有aggregateBy方法吗?和流API对象的Sum属性

但在这两种情况下,最终结果都是一个映射,而不是一个列表

根据@AndrewTobilko和@JBNizet的回答,我使用的最终解决方案是:

List<MyClass> myClassList = list.stream()
    .collect(Collectors.groupingBy(YourClass::getCategory,
                    Collectors.summingInt(YourClass::getValue)))
    .entrySet().stream().map(e -> new MyClass(e.getKey(), e.getValue()).collect(toList());

collections类提供了一个'groupingBy',允许您在流上执行'group by'操作(类似于数据库中的group by)。假设你的对象列表是' objects '类型,下面的代码应该可以工作:

Map<String, Integer> valueByCategory = myObjects.stream().collect(Collectors.groupingBy(MyObjects::getCategory, Collectors.summingInt(MyObjects::getValue)));

代码基本上按每个类别对流进行分组,并在每个组上运行一个收集器,该收集器汇总每个流元素的getValue()的返回值。见https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html

静态导入Collectors类:

list.stream().collect(groupingBy(Class::getCategory, summingInt(Class::getValue)));

你将得到一张地图Map<String, Integer>Class必须有getValuegetCategory方法来编写方法引用,例如

public class Class {
    private String category;
    private int value;
    public String getCategory() { return category; }
    public int getValue() { return value; }
}

基于reduce的方法:

List<Obj> values = list.stream().collect(
        Collectors.groupingBy(Obj::getCategory, Collectors.reducing((a, b) -> new Obj(a.getValue() + b.getValue(), a.getCategory())))
).values().stream().map(Optional::get).collect(Collectors.toList());

糟糕的是次要stream()调用重新映射Optional<Obj>和中间Map<String, Optional<Obj>>对象的结果。

我可以建议使用排序的替代变体(可读性较差):

List<Obj> values2 = list.stream()
    .sorted((o1, o2) -> o1.getCategory().compareTo(o2.getCategory()))
    .collect(
        LinkedList<Obj>::new,
        (ll, obj) -> {
            Obj last = null;
            if(!ll.isEmpty()) {
                last = ll.getLast();
            }
            if (last == null || !last.getCategory().equals(obj.getCategory())) {
                ll.add(new Obj(obj.getValue(), obj.getCategory())); //deep copy here
            } else {
                last.setValue(last.getValue() + obj.getValue());
            }
        },
        (list1, list2) -> {
              //for parallel execution do a simple merge join here
              throw new RuntimeException("parallel evaluation not supported"); 
         }
    );

这里我们按类别对Obj s列表进行排序,然后按顺序处理,压缩来自同一类别的连续对象。

不幸的是,如果不手动保存最后一个元素或元素列表,Java中没有方法可以做到这一点(参见从流中收集连续的对)

两个片段的工作示例可以在这里查看:https://ideone.com/p3bKV8

相关内容

  • 没有找到相关文章

最新更新