【问题标题】:Java Stream Generate Map from List of ObjectsJava Stream 从对象列表生成地图
【发布时间】:2020-07-01 03:28:13
【问题描述】:

我有这样的课。

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

给定一个 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 ));

我需要返回请求一个对象按前缀 asc 排序,如下所示:

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

所以问题是: 我必须按前缀对列表进行分组,然后显示每个扇区以及列表中具有相同行和扇区的项目的计数(*)。 我得到的远是使用这样的流:

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

问题是,上面的代码是计数是长的,我需要作为字符串返回。 我试过 .toString 但它给了我一个错误(可以将 java.lang.String 分配给 java.util.stream.Collector)。

更新

在 Andreas 和 Naman 的帮助下,现在我可以将 count 映射为 String。 我只需要按前缀排序。

谁能帮帮我?

【问题讨论】:

  • 执行Collectors.counting().toString() 将具有String 类型并将其放在预期Collector 的位置会引发编译时错误。使用 Collectors.collectingAndThen(Collectors.counting(), String::valueOf) 会帮助你。
  • 谢谢,纳曼。我怎样才能让它按前缀排序?
  • @ErickDeMirandaOliveira - 如果我的回答有帮助,请告诉我。

标签: java list java-stream collectors


【解决方案1】:

你快到了,只需将 Collectors.mapping 行替换为:

Collectors.summingInt(Foo::getCount))

如:

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

注意TreeMap 构造函数添加到groupingBy() 调用中,这确保了对映射的排序。第一个是按字典顺序排序,而第二个是根据口语排序,即大写与小写不影响顺序。

【讨论】:

  • 嗨安德烈亚斯。但现在它返回一个 Integer 而不是 Long。我需要的是作为字符串返回的计数。请注意,我忘记了问题上的“”。我会编辑。你还能帮我吗?
  • @ErickDeMirandaOliveira 好的,将 count 的总和值更改为 String
  • Tks 眉毛!!你救了我的命
  • 最后一个问题。如何按前缀排序?
  • @ErickDeMirandaOliveira 对问题进行此类编辑(更新)而不是 cmets 很好。请注意Collectors.countingCollectors.summingInt(Foo::getCount) 不同,只是碰巧输入的值count 在当前示例中设置为1,其中两者的总计数相同。
【解决方案2】:

这是你需要的吗?

代码:

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

}

输出:

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

PS - 我提到了this tutorial 来回答你的问题。我参考了“2.5. 按多个字段分组”和“2.7. 从分组结果中获取总和”中的示例。下一步是找出如何在分组时通过地图的键进行排序,我从“2.11。修改返回地图类型”中得到并检查了它here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-12
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多