简体   繁体   English

Java Stream 从对象列表生成映射

[英]Java Stream Generate Map from List of Objects

I have a class like this.我有一堂这样的课。

public class Foo {
    private String prefix;
    private String sector;
    private int count;
}

Given a foo list:给定一个 foo 列表:

//Args: prefix, sector, count
fooList.add(new Foo("44",,"RowC", 1 ));
fooList.add(new Foo("1",,"Rowa", 1 ));
fooList.add(new Foo("1",,"RowB", 1 ));
fooList.add(new Foo("1",,"Rowa", 1 ));

And I need to return the request an object sorted by Prefix asc like this:我需要返回一个按 Prefix asc 排序的对象的请求,如下所示:

{
  "1": {
    "Rowa": "2",
    "RowB": "1"
  },
  "44": {
    "RowC": "1"
  }
}

So the problem is: I have to group the list by the prefix, and then show, every sector and the count(*) of items on the list with the same row and sector.所以问题是:我必须按前缀对列表进行分组,然后显示每个扇区和列表中具有相同行和扇区的项目的计数(*)。 The far that I got is using stream like this:我得到的远是使用这样的流:

fooList.stream()
       .collect(Collectors.groupingBy(
                Foo::getPrefix,
                Collectors.groupingBy(
                        Foo::getSector,
                        Collectors.mapping(Foo::getSector , Collectors.counting())
                )
        ));

The problem is, that the code above, is that the count is a Long, and I need to return as a String.问题是,上面的代码是计数是一个长整数,我需要作为一个字符串返回。 I've tried with .toString but it gives me an error (Can assign java.lang.String to java.util.stream.Collector).我试过 .toString 但它给了我一个错误(可以将 java.lang.String 分配给 java.util.stream.Collector)。

UPDATE更新

With the help of Andreas and Naman, now I can map count as String.在 Andreas 和 Naman 的帮助下,现在我可以将 count 映射为 String。 I just need it sorted by Prefix.我只需要按前缀排序。

Can anyone help me?谁能帮我?

You were almost there, just replace the Collectors.mapping line with:你快到了,只需将Collectors.mapping行替换为:

Collectors.summingInt(Foo::getCount))

As in:如:

List<Foo> fooList = new ArrayList<>();
fooList.add(new Foo("44", "RowC", 1 ));
fooList.add(new Foo("1", "Rowa", 1 ));
fooList.add(new Foo("1", "RowB", 1 ));
fooList.add(new Foo("1", "Rowa", 1 ));

Map<String, Map<String, String>> result = fooList.stream().collect(
        Collectors.groupingBy(
                Foo::getPrefix,
                TreeMap::new, // remove if prefix sorting not needed
                Collectors.groupingBy(
                        Foo::getSector,
                        () -> new TreeMap<>(Collator.getInstance()), // remove if sector sorting not needed
                        Collectors.collectingAndThen(
                                Collectors.summingInt(Foo::getCount),
                                String::valueOf
                        )
                )
        )
);

System.out.println(result); // prints: {1={Rowa=2, RowB=1}, 44={RowC=1}}

Notice the TreeMap constructors added to the groupingBy() calls, which ensures that the maps are sorted.请注意添加到groupingBy()调用中的TreeMap构造函数,它确保对映射进行排序。 The first is sorting lexicographically , while the second sorts according to spoken language, ie upper- vs lowercase doesn't affect order.第一个是按字典顺序排序,而第二个根据口语排序,即大写与小写不影响顺序。

Is this what you need?这是你需要的吗?

Code :代码 :

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.summingInt;

public class Main {
    public static void main(String [] args){
        List<Foo> fooList = new ArrayList<>();
        fooList.add(new Foo("44", "RowC", 1 ));
        fooList.add(new Foo("1", "Rowa", 1 ));
        fooList.add(new Foo("1", "RowB", 1 ));
        fooList.add(new Foo("1", "Rowa", 1 ));

        Map<String, Map<String, Integer>> result = grouper(fooList);
        result.forEach( (k,v) -> System.out.printf("%s\n%s\n", k,v) );
    }

    /* group the list by the prefix, and then show, every sector and the
    *  count(*) of items on the list with the same row and sector.*/
    public static Map<String, Map<String, Integer>> grouper(List<Foo> foos){
        //Map<prefix, Map<sector, count>
        Map<String, Map<String, Integer>> result = foos.stream()
                .collect(
                        //Step 1 - group by prefix. Outer map key is prefix.
                        groupingBy( Foo::getPrefix,
                                //Use a tree map which wil be sorted by its key, i.e prefix.
                                TreeMap::new,
                                //Step 2 - group by sector.
                                groupingBy( Foo::getSector,
                                        //Step 3 - Sum the Foo's in each sector in each prefix.
                                        summingInt(Foo::getCount)
                                )
                        )
                );
        return result;
    }

}

Output :输出 :

1
{Rowa=2, RowB=1}
44
{RowC=1}

PS - I referred to this tutorial to answer your question. PS - 我参考本教程来回答您的问题。 I referred to the examples in " 2.5. Grouping by Multiple Fields " and " 2.7. Getting the Sum from Grouped Results ".我参考了“ 2.5. 按多个字段分组”和“ 2.7. 从分组结果中获取总和”中的示例。 The next step was to find out how to also order by the key of a map while grouping which I got from " 2.11. Modifying the Return Map Type " and also checked it here .下一步是找出如何在分组时按地图键进行排序,这是我从“ 2.11. 修改返回地图类型”中获得的,并在此处进行了检查。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM