Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/364.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java8流GroupByPOJO_Java_Java 8_Java Stream_Collectors - Fatal编程技术网

Java8流GroupByPOJO

Java8流GroupByPOJO,java,java-8,java-stream,collectors,Java,Java 8,Java Stream,Collectors,我有一个POJO的集合: public class Foo { String name; String date; int count; } 我需要按名称和总和计数遍历集合、groupbyfoos,然后使用具有总和计数的pojo创建新集合 我现在是这样做的: List<Foo> foosToSum = ... Map<String, List<Foo>> foosGroupedByName = foosToSum.s

我有一个POJO的集合:

public class Foo {
    String name;
    String date;
    int count;
}
我需要按名称和总和计数遍历集合、groupbyfoos,然后使用具有总和计数的pojo创建新集合

我现在是这样做的:

    List<Foo> foosToSum = ...

    Map<String, List<Foo>> foosGroupedByName = foosToSum.stream()
            .collect(Collectors.groupingBy(Foo::getName));

    List<Foo> groupedFoos = foosGroupedByName.keySet().stream().map(name -> {
        int totalCount = 0;
        String date = "";
        for(Foo foo: foosGroupedByName.get(name)) {
            totalCount += foo.getCount();
            date = foo.getDate() //last is used
        }
        return new Foo(name, date, totalCount);
    }).collect(Collectors.toList());
List foosToSum=。。。
Map foosGroupedByName=foosToSum.stream()
.collect(Collectors.groupingBy(Foo::getName));
List groupedFoos=foosGroupedByName.keySet().stream().map(名称->{
int totalCount=0;
字符串日期=”;
for(Foo-Foo:foosGroupedByName.get(name)){
totalCount+=foo.getCount();
date=foo.getDate()//使用最后一个
}
返回新的Foo(名称、日期、总数);
}).collect(Collectors.toList());
有没有更美的方法来处理溪流

更新感谢大家的帮助。所有答案都很好。 我决定在pojo中创建合并函数

最终的解决方案如下所示:

Collection<Foo> groupedFoos = foosToSum.stream()
                    .collect(Collectors.toMap(Foo::getName, Function.identity(), Foo::merge))
                    .values();
Collection groupedFoos=foosToSum.stream()
.collect(Collectors.toMap(Foo::getName,Function.identity(),Foo::merge))
.values();

是的,您可以在您的
分组方式中使用下游收集器来立即对计数求和。之后,流式处理map并将其映射到Foos

foosToSum.stream()
         .collect(Collectors.groupingBy(Foo::getName,
                                        Collectors.summingInt(Foo::getCount)))
         .entrySet()
         .stream()
         .map(entry -> new Foo(entry.getKey(), null, entry.getValue()))
         .collect(Collectors.toList());

一个更高效的解决方案可以避免分组到一个映射中,而只是立即流式处理,但会牺牲一些可读性(在我看来):


通过减少Foo而不是int,我们可以记住名称,并可以立即将其相加为Foo。

您可以使用
groupingBy
toMap
收集器来实现这一点,因为使用哪一个是有争议的,所以我会让您决定您喜欢哪一个

为了更好的可读性,我将在
Foo
中创建一个合并函数,并将所有合并逻辑隐藏在其中

这也意味着更好的可维护性,因为合并越复杂,您只需更改一个位置,即
merge
方法,而不是流查询

e、 g

现在您可以执行以下操作:

Collection<Foo> resultSet = foosToSum.stream()
            .collect(Collectors.toMap(Foo::getName,
                    Function.identity(), Foo::merge)).values();
此外,如果出于某种原因明确要求使用
列表
而不是
集合
,则可以使用
ArrayList
复制构造函数来完成

List<Foo> resultList = new ArrayList<>(resultSet);
List resultList=newarraylist(resultSet);
更新

正如@Federico在评论中提到的,上面的最后一个合并函数非常昂贵,因为它创建了可以避免的不必要的对象。因此,正如他所建议的,一个更友好的替代方法是继续使用我上面展示的第一个合并函数,然后将您的流查询更改为:

Collection<Foo> resultSet = foosToSum.stream()
                .collect(Collectors.toMap(Foo::getName,
                        f -> new Foo(f.getName(), null, f.getCount()), Foo::merge))
                .values();
Collection resultSet=foosToSum.stream()
.collect(Collectors.toMap)(Foo::getName,
f->new Foo(f.getName(),null,f.getCount()),Foo::merge))
.values();

很好的解决方案。但是,我需要以某种方式从名为Foos的分组中获取最新日期字段。我的错误是,我已经写过了,这并不重要,但确实重要。好吧,您可以尝试使用
foo2.getDate()
而不是
null
。但是你要依赖的是仍然在订购的食物。如果您想确定,可以使用类似于
getLater(foo1.getDate(),foo2.getDate())
的东西。正如Aominè在另一个答案中所说的,您可以将所有的逻辑放在Foo类中并编写
…reduce(new Foo(),Foo::merge)
Hi Aominè!只是一个评论。。。如果使用
Function.identity()
作为值映射器函数,则源集合的元素发生变化是正确的。因此,您建议在合并时创建新的
Foo
元素。虽然这是正确的,但它会创建不必要的对象。您可以在值映射器函数中创建一个副本(只需使用一个副本构造函数),然后在此副本上进行合并,就像您原来的
merge
方法一样。您好,@FedericoPeraltaSchaffner我刚才看到了您的评论,但无法回复。现在我是:)。无论如何,你是对的,我已经相应地更新了我的答案。谢谢你的反馈。嘿,奥米内太好了,你已经更新了你已经接受的答案;)我已经提前投票:)@FedericoPeraltaSchaffner事实上,再看看我的代码,它只在出现键冲突时创建了新对象,在这种情况下,没有冲突,它仍然会从源代码维护特定对象。所以你不仅改进了我的代码,还解决了一个微妙的问题:)。奥米内,是的,你在这方面也是正确的。我没有注意到。那好吧。我们一石二鸟。。。
public Foo merge(Foo another){
      return new Foo(this.getName(), null, this.getCount() + another.getCount());
}
List<Foo> resultList = new ArrayList<>(resultSet);
Collection<Foo> resultSet = foosToSum.stream()
                .collect(Collectors.toMap(Foo::getName,
                        f -> new Foo(f.getName(), null, f.getCount()), Foo::merge))
                .values();