java 8 stream group by sum bigdecimal

Java8 stream 中利用 groupingBy 进行多字段分组求和案例

Java8的groupingBy实现集合的分组,类似Mysql的group by分组功能,注意得到的是一个map

对集合按照单个属性分组、分组计数、排序

List<String> items =
    Arrays.asList("apple", "apple", "banana",
        "apple", "orange", "banana", "papaya");
// 分组
Map<String, List<String>> result1 = items.stream().collect(
    Collectors.groupingBy(
        Function.identity()
//{papaya=[papaya], orange=[orange], banana=[banana, banana], apple=[apple, apple, apple]}
System.out.println(result1);
// 分组计数
Map<String, Long> result2 = items.stream().collect(
    Collectors.groupingBy(
        Function.identity(), Collectors.counting()
// {papaya=1, orange=1, banana=2, apple=3}
System.out.println(result2);
Map<String, Long> finalMap = new LinkedHashMap<>();
//分组, 计数和排序
result2.entrySet().stream()
    .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
    .forEachOrdered(e -> finalMap.put(e.getKey(), e.getValue()));
// {apple=3, banana=2, papaya=1, orange=1}
System.out.println(finalMap);

集合按照多个属性分组

1.多个属性拼接出一个组合属性

public static void main(String[] args) {
  User user1 = new User("zhangsan", "beijing", 10);
  User user2 = new User("zhangsan", "beijing", 20);
  User user3 = new User("lisi", "shanghai", 30);
  List<User> list = new ArrayList<User>();
  list.add(user1);
  list.add(user2);
  list.add(user3);
  Map<String, List<User>> collect = list.stream().collect(Collectors.groupingBy(e -> fetchGroupKey(e)));
  //{zhangsan#beijing=[User{age=10, name='zhangsan', address='beijing'}, User{age=20, name='zhangsan', address='beijing'}], 
  // lisi#shanghai=[User{age=30, name='lisi', address='shanghai'}]}
  System.out.println(collect);
private static String fetchGroupKey(User user){
  return user.getName() +"#"+ user.getAddress();

2.嵌套调用groupBy

User user1 = new User("zhangsan", "beijing", 10);
User user2 = new User("zhangsan", "beijing", 20);
User user3 = new User("lisi", "shanghai", 30);
List<User> list = new ArrayList<User>();
list.add(user1);
list.add(user2);
list.add(user3);
Map<String, Map<String, List<User>>> collect
    = list.stream().collect(
        Collectors.groupingBy(
            User::getAddress, Collectors.groupingBy(User::getName)
System.out.println(collect);
  • 使用Arrays.asList
  • 我有一个与Web访问记录相关的域对象列表。这些域对象可以扩展到数千个。

    我没有资源或需求将它们以原始格式存储在数据库中,因此我希望预先计算聚合并将聚合的数据放在数据库中。

    我需要聚合在5分钟窗口中传输的总字节数,如下面的sql查询

    select 
     round(request_timestamp, '5') as window, --round timestamp to the nearest 5 minute
     http_result_code, 
     transaction_time, 
     sum(bytes_transferred)
    from web_records
    group by 
      round(request_timestamp, '5'), 
      http_result_code, 
      transaction_time
    

    在java 8中,我当前的第一次尝试是这样的,我知道这个解决方案类似于Group by multiple field names in java 8

    Map<Date, Map<String, Map<String, Map<String, Map<String, Integer>>>>>>> aggregatedData =
    webRecords
      .stream()
      .collect(Collectors.groupingBy(WebRecord::getFiveMinuteWindow,
            Collectors.groupingBy(WebRecord::getCdn,
             Collectors.groupingBy(WebRecord::getIsp,
              Collectors.groupingBy(WebRecord::getResultCode,
                Collectors.groupingBy(WebRecord::getTxnTime,
                 Collectors.reducing(0,
                           WebRecord::getReqBytes(),
                           Integer::sum)))))));
    

    这是可行的,但它是丑陋的,所有这些嵌套的地图是一个噩梦!要将地图“展平”或“展开”成行,我必须这样做

    for (Date window : aggregatedData.keySet()) {
     for (String cdn : aggregatedData.get(window).keySet()) {
      for (String isp : aggregatedData.get(window).get(cdn).keySet()) {
       for (String resultCode : aggregatedData.get(window).get(cdn).get(isp).keySet()) {
        for (String txnTime : aggregatedData.get(window).get(cdn).get(isp).get(resultCode).keySet()) {
          Integer bytesTransferred = aggregatedData.get(window).get(cdn).get(distId).get(isp).get(resultCode).get(txnTime);
          AggregatedRow row = new AggregatedRow(window, cdn, distId...
                  zhouxineric