Java Stream流实现多字段分组groupingBy操作

近期的项目里,遇到一个需求:对于含有多个元素的List,按照其中的某几个属性进行分组,比如Report::getPersonID、Report::getSchoolYear、Report::getDataType等字段。下面就让我们讨论一下如何比较优雅的按多字段进行分组groupingBy。

  1. 利用单个字段进行分组
    如上面的Report类,如果对于其中的某一个字段进行分组(如PersonID),则比较简单,我们可以利用Stream.collect()和Collectors.groupingBy结合,即可进行分组groupingBy,代码如下:
public class TestGroupingBy {
    public static void main(String[] args) {
        List<Report> reportList = Arrays.asList(
                new Person().setPersonID(1).setSchoolYear(2022).setDataType("本科")
                //这里添加其他的对象,可以多添加一些
        );
        Map<String, List<Report>> groupingMap = reportList.stream().collect(Collectors.groupingBy(Person::getPersonID));
}

其中的groupingMap ,类型为Map<String, List>,第一个泛型为String即分组字段(本例中为personID字段)的类型,第二个泛型为List及分组结果的类型。
2. 利用多个字段进行分组
上面的例子是按单个字段分组,如果需要按照多个字段,如personID、schoolYear、dataType三个字段进行分组,同样也可以可以利用Stream.collect()和Collectors.groupingBy结合的方式进行分组,不过该方式中调用Collectors.groupingBy时需要多次嵌套调用,测试代码如下:

public class TestGroupingBy {
    public static void main(String[] args) {
        List<Report> reportList = Arrays.asList(
                new Person().setPersonID(1).setSchoolYear(2022).setDataType("本科")
                //这里添加其他的对象,可以多添加一些
        );
        // 多字段嵌套分组
        Map<String, Map<Integer, Map<String, List<Report>>>> groupingMap = personList.stream().collect(
                Collectors.groupingBy(Report::getPersonID, 
                        Collectors.groupingBy(Report::getSchoolYear, 
                                Collectors.groupingBy(Report::getDataType)
                        )
                )
        );
    }
}

其中groupingMap类型为Map<String, Map<Integer, Map<String, List>>>,是一个嵌套了三层的Map,对应的泛型String/Integer/String分别为对应分组字段的类型,最后一层Map的value类型为List为实际分组后的数据集合类型。为方便查看数据,特意按Json格式贴出数据如下:

{
  "1": {
    "2022": {
      "本科": [
        {
          "id": 1,
          "schoolYear": 20,
          "dataType": "本科"
        }
      ]
      			}
      }
}
  1. 利用Collectors.groupingBy与Function结合对多字段分组进行优化
public class TestGroupingBy {
    public static void main(String[] args) {
        List<Report> reportList = Arrays.asList(
                new Person().setPersonID(1).setSchoolYear(2022).setDataType("本科")
        );
        // 定义一个函数Function,该函数将元素对象映射到一个键的集合里
        Function<Report, List<Object>> compositeKey = report->
                Arrays.asList(report.getPersonID(), report.getSchoolYear(), report.getDataType());
        // 分组
        Map<List<Object>, List<Report>> groupingMap =
                reportList.stream().collect(Collectors.groupingBy(compositeKey, Collectors.toList()));
    }
}

groupingMap数据仅仅只有一层,但是其键值Key却是一个List,里面包含了分组字段的值,数据按Json格式贴出如下:

{
  "[1, 2022, 本科]": [
    {
          "id": 1,
          "schoolYear": 20,
          "dataType": "本科"
    }
  ]
}

由于Map只有一层,用该方式分组的结果,对于我们业务也是比较友好,代码里对数据处理起来也是比较方便的。可以看到,从代码书写角度以及分组处理后得到的结果,该方法都是最优雅的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 8中,使用Stream的Collectors.groupingBy方法可以对数据进行分组操作。但是需要注意的是,如果分组字段数据存在丢失的情况,可能是由于以下原因导致的: 1. 分组字段数据类型不一致:如果分组字段数据类型不一致,可能会导致分组数据丢失。例如,如果分组字段是一个对象的属性,而该属性的数据类型在不同的对象中不一致,那么在分组时可能会导致某些数据被丢弃。 2. 分组字段的hashCode和equals方法未正确重写:在进行分组操作时,需要使用分组字段的hashCode和equals方法来确定分组的依据。如果这两个方法未正确重写,可能会导致分组数据丢失。 为了避免数据丢失的情况发生,可以采取以下措施: 1. 确保分组字段数据类型一致:在进行分组操作之前,可以先对分组字段数据类型进行统一,保它们具有相同的数据类型。 2. 重写分组字段的hashCode和equals方法:如果分组字段是一个自定义对象的属性,需要确保该属性的hashCode和equals方法已正确重写,以确保分组操作的准确性。 下面是一个示例代码,演示了如何使用Java 8的Stream和Collectors.groupingBy方法进行分组操作: ```java import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class GroupingByDemo { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eva"); // 按照名字的首字母进行分组 Map<Character, List<String>> groups = names.stream() .collect(Collectors.groupingBy(name -> name.charAt(0))); // 输出分组结果 groups.forEach((key, value) -> System.out.println(key + ": " + value)); } } ``` 运行以上代码,将会按照名字的首字母进行分组,并输出分组结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值