当前位置: 首页 > 知识库问答 >
问题:

根据流中的另一个字段汇总字段[重复]

长孙诚
2023-03-14

我有一个如下所示的对象列表:

{
    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"
}

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

我应该使用平面图吗?减少我不明白这些的细微差别。

帮助

编辑:

这个问题有密切的重复:流中是否有聚合通过方法Java8 api?和带有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());

共有3个答案

堵彬彬
2023-03-14

基于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());

不好的是,从中重新映射结果的辅助调用是可选的

我可以使用排序建议其他变体(可读性较差):

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"); 
         }
    );

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

不幸的是,Java没有方法在不手动保留最后一个元素或元素列表的情况下做到这一点(另请参阅从流中收集连续对)

可以在此处检查使用这两个片段的工作示例:https://ideone.com/p3bKV8

巫马承德
2023-03-14

使用Collection类的静态导入:

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

你会得到一张地图

public class Class {
    private String category;
    private int value;

    public String getCategory() { return category; }
    public int getValue() { return value; }
}
钮兴安
2023-03-14

Collection类提供了一个“groupingBy”,允许您对流执行“group by”操作(类似于数据库中的GROUP BY)。假设您的对象列表是“Object”类型,以下代码应该可以工作:

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

 类似资料:
  • 我想格式化一个<code>java.time。LocalTime,但格式可以根据其值而变化: 如果一天中的小时数为12或0,请使用格式 否则,请使用格式 我当然可以这样做: 但为此,我需要创建两个不同的格式化程序。 我只想使用一个可以多次重用的格式化程序: 我正在尝试使用来做到这一点: 我尝试使用< code > datetimeformatterbuilder . optional start(

  • 问题内容: 我想知道模式草案03是否可行。我已经在其他地方使用了依赖项,我认为可能需要创造性地使用它们,以便使用它们来指定某些字段的属性。 我目前的最佳尝试(无效)将使您对我的追求有所了解。我想要一个默认值,当另一个字段具有特定值时是可选值。 问题答案: 草案的第3版绝对可以做到这一点。由于您具有允许的国家/地区的完整列表,因此您可以执行以下操作: 因此,您实际上为架构定义了两种子类型,一种用于需

  • 我有一个正在JSP中构建的Spring MVC表单,它将需要在字段中输入一个帐号。当我输入该数字时,我想对数据库运行一个查询,以拉回该特定数字的相关信息。然后,此数据将填充表单上的其他字段。 这是我在JSP中为将要输入的帐号编写的Spring绑定代码。因此,只要我输入这个数字,就会触发一个DB查询,将其他字段的数据带回来。 戴夫

  • 是否可以重载一个字段类型成为另一个字段类型? 如果是,是否可以提供一些例子?

  • 问题内容: 我正在处理一些旧代码/数据库,需要在数据库中添加一个字段,该字段将记录与该(外部)ID相关的序列号。 表格数据示例(当前): 我需要添加一个sequenceid列,该列分别为每个帐户递增,以实现: 请注意,该顺序与帐户有关。 有什么方法可以用SQL来实现,还是可以使用PHP脚本为我完成工作? TIA,Kev 问题答案: 这应该可以,但是可能很慢: 我称该表为“测试”;显然,需要正确设置

  • 问题内容: 所以我有: 是否有一条sql语句将所有内容从id复制到number? 我将要编写一个php scrip进行选择,然后更新每一行。我的SQL知识很基础,但是我敢肯定有一个聪明的方法可以做到这一点: 背景:ID过去一直是应用程序中显示的数字,并且是唯一的。该数字在我要添加的一些新功能中不再是唯一的-因此我需要将其移动到不唯一的字段中,同时保持旧的完整性。 问题答案: 您可以使用更新语句并引