我有一个对象列表,看起来像这样:
{
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
必须有getValue
和getCategory
方法来编写方法引用,例如
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