我有一个对象列表,如下所示:
{
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吗?降低?我不明白这些的细微差别来弄明白。
帮助?
编辑:
这个问题有很多重复: Is there an aggregateBy method in the stream Java 8 api? 和 Sum attribute of object with Stream API
但在这两种情况下,最终结果都是地图,而不是列表
根据@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());
答案 0 :(得分:5)
Collectors类提供了一个“groupingBy”,允许您对流执行“分组依据”操作(类似于数据库中的GROUP BY)。假设您的对象列表是“对象”类型,以下代码应该起作用:
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
答案 1 :(得分:2)
使用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; }
}
答案 2 :(得分:0)
基于减少的方法:
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
的列表进行排序,然后按顺序处理它,压缩来自同一类别的连续对象。
不幸的是,Java中没有方法可以在不手动保留最后一个元素或元素列表的情况下执行此操作(另请参阅Collect successive pairs from a stream)
可以在此处检查两个代码段的工作示例:https://ideone.com/p3bKV8