我的编程空间,编程开发者的网络收藏夹
学习永远不晚

使用Java8进行分组(多个字段的组合分组)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

使用Java8进行分组(多个字段的组合分组)

在SQL中经常会用到分组,我们也常常遇到一些组合分组的场景。

有下面的一个User类

import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@Builder
public class User {
    private String name;
    private int id;
    private String city;
    private String sex;
    private LocalDateTime birthDay;
}

java8分组 传统写法(单个字段分组)

场景:根据 城市 进行分组

使用的是方法引用:User::getCity 来完成分组

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Demo2 {
    public static void main(String[] args) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // data list
        List<User> userList = Arrays.asList(
                User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build()
        );
        Map<String, List<User>> groupMap = userList.stream()
        	.collect(Collectors.groupingBy(User::getCity));
        groupMap.forEach((k, v) -> {
            System.out.println(k);
            System.out.println(v);
        });
    }
}

java8分组 传统写法(多个字段分组)

场景:根据 城市,性别进行分组

一般的写法会是下面的这种写法,通过lambda表达式将key的生成逻辑传入进去:u -> u.getCity() + "|" + u.getSex() 来实现分组的效果。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Demo2 {
    public static void main(String[] args) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // data list
        List<User> userList = Arrays.asList(
                User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build()
        );
        Map<String, List<User>> groupMap = userList.stream()
        	.collect(Collectors.groupingBy(u -> u.getCity() + "|" + u.getSex()));
        groupMap.forEach((k, v) -> {
            System.out.println(k);
            System.out.println(v);
        });
    }
}

分析:多个分组条件 与 单个分组条件 两种写法

单个条件的分组用的比较多,userList.stream().collect(Collectors.groupingBy(User::getCity));
这种方法引用的方式看起来很清爽。
在我们遇到多个字段的分组的时候,我并不太想使用前面那种传统的写法①。

我在想,既然单个字段的分组写法是:

userList.stream().collect(Collectors.groupingBy(User::getCity));

那么多个字段的写法可否是下面这种( 类推 ),传入多个方法引用!

userList.stream().collect(Collectors.groupingBy(User::getCity,User::getSex));

很可惜 jdk 类库中Collectors 没有提供这种写法

多个字段的优雅写法

因为jdk没有提供这种写法,于是自己就想写了一个Util来帮助我们使用多个方法引用的方式完成组合分组

MyBeanUtil groupingBy(userList, User::getSex, User::getCity);

Demo:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class MyBeanUtil {

    public static void main(String[] args) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // data list
        List<User> userList = Arrays.asList(
                User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build()
        );
        // 进行分组,根据名字和城市分组
        Map<String, List<User>> groupMap = groupingBy(userList, User::getSex, User::getCity);
        //打印分组结果
        groupMap.forEach((k, v) -> {
            System.out.println(k);
            System.out.println(v);
        });
    }
    
    @SafeVarargs
    public static <T, R> Map<String, List<T>> groupingBy(List<T> list, Function<T, R>... functions) {
        return list.stream().collect(Collectors.groupingBy(t -> groupingBy(t, functions)));
    }

    
    @SafeVarargs
    public static <T, R> String groupingBy(T t, Function<T, R>... functions) {
        if (functions == null || functions.length == 0) {
            throw new NullPointerException("functions数组不可以为空");
        } else if (functions.length == 1) {
            return functions[0].apply(t).toString();
        } else {
            return Arrays.stream(functions).map(fun -> fun.apply(t).toString()).reduce((str1, str2) -> str1 + "|" + str2).get();
        }
    }
}

再度优化

依然不是很满足这种写法,因为这种写法需要借助 Util 类,不够接地气!
我更希望是下面这种接地气的写法:能够完全集成在jdk类库中

userList.stream().collect(Collectors.groupingBy(User::getCity,User::getSex));

为了达到上述的效果,那么显然我们是需要修改jdk源代码的;
于是我就将java.util.stream.Collectors源码完整copy出来,然后加入下面3个方法

    public static <T, K> Collector<T, ?, HashMap<K, List<T>>>
    groupingBy(Function<? super T, ? extends K>... classifier) {
        return groupingBy("|", classifier);
    }
    
    public static <T, K> Collector<T, ?, HashMap<K, List<T>>>
    groupingBy(String split, Function<? super T, ? extends K>... classifier) {
        return groupingBy(split, classifier, HashMap::new, toList());
    }
    
    public static <T, K, D, A, M extends Map<? super K, D>>
    Collector<T, ?, M> groupingBy(String split,
                                  Function<? super T, ? extends K>[] classifierArr,
                                  Supplier<M> mapFactory,
                                  Collector<? super T, A, D> downstream) {
        Supplier<A> downstreamSupplier = downstream.supplier();
        BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
        BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
            String key = Arrays.stream(classifierArr).map(classifier -> Objects.requireNonNull(classifier.apply(t))).map(String::valueOf).reduce((s1, s2) -> s1 + split + s2).get();
            A container = m.computeIfAbsent((K) key, k -> downstreamSupplier.get());
            downstreamAccumulator.accept(container, t);
        };
        BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner());
        @SuppressWarnings("unchecked")
        Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory;

        if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
            return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);
        } else {
            @SuppressWarnings("unchecked")
            Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
            Function<Map<K, A>, M> finisher = intermediate -> {
                intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
                @SuppressWarnings("unchecked")
                M castResult = (M) intermediate;
                return castResult;
            };
            return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
        }
    }

就达到了我们预期的效果,为了方便大家也一起体验一下,我已经将demo完整的放到了github上

源码地址:https://github.com/1015770492/CollectorsDemo

下载好源码后,找到下面这个类

在这里插入图片描述

Demo:

import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class MultiGroupByDemo {


    public static void main(String[] args) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // data list
        List<User> userList = Arrays.asList(
                User.builder().id(123456).name("Zhang, San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(777777).name("Zhang, San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(888888).name("Li, Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(999999).name("Zhan, San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build(),
                User.builder().id(555555).name("Li, Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-01 12:00:00", df)).build()
        );
        

        // 1.Use the default vertical separator
        System.out.println("Use the default vertical separator:");
        HashMap<String, List<User>> defaultSpilt = userList.stream().collect(Collectors.groupingBy(User::getName, User::getCity));
        printMap(defaultSpilt);
        System.out.println();

        // 2.Use custom delimiters
        System.out.println("Use custom delimiters:");
        userList.stream().collect(Collectors.groupingBy("--", User::getName, User::getCity, User::getId));
        HashMap<? extends Serializable, List<User>> collect = userList.stream().collect(Collectors.groupingBy("--", User::getName, User::getCity, User::getId));
        printMap(collect);
        System.out.println();

        // 3.Use custom delimiters
        System.out.println("Use custom delimiters:");
        userList.stream().collect(Collectors.groupingBy("--", User::getName, User::getCity, User::getId));
        HashMap<? extends Serializable, List<User>> collect2 = userList.stream().collect(Collectors.groupingBy(User::getName, User::getCity, User::getBirthDay));
        printMap(collect2);


    }

    public static <T> void printMap(Map<? extends Serializable, List<T>> map){
        map.forEach((k, v) -> {
            System.out.println(k);
            System.out.println(v);
        });
    }

}

在这里插入图片描述

最后我希望这个特性能被JDK所吸收,这样可以方便大家更好的使用这些好用的特性

到此这篇关于使用Java8进行分组(多个字段的组合分组)的文章就介绍到这了,更多相关Java8 分组内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

使用Java8进行分组(多个字段的组合分组)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

mysql group by 对多个字段进行分组操作

在平时的开发任务中我们经常会用到MYSQL的GROUP BY分组, 用来获取数据表中以分组字段为依据的统计数据。 比如有一个学生选课表,表结构如下:Table: Subject_Selection Subject Semester At
2022-05-16

按照多个字段的先后顺序进行分组

groupby多个字段先后顺序,需要具体代码示例在数据处理和分析中,常常需要对数据进行分组,并按照多个字段的先后顺序进行分组操作。今天,我们将介绍如何使用Python中的pandas库来实现多字段的groupby操作,并提供具体的代码示例
按照多个字段的先后顺序进行分组
2024-02-22

PHP 数组按多个字段分组的方法详解

如何按多个字段分组 php 数组?array_column():按指定字段分组,提取该字段的值作为键。array_multisort():按多个字段排序数组,对相邻的重复行分组。自定义函数:定义自定义函数,使用循环按指定字段分组数组。PHP
PHP 数组按多个字段分组的方法详解
2024-05-04

List集合按某个属性或者字段进行分组的示例分析

这篇文章给大家分享的是有关List集合按某个属性或者字段进行分组的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。List集合按某个属性或者字段进行分组List分组 按照Student对象中
2023-06-15

SQL多个字段拼接组合成新字段的常用方法

本文详细介绍了在SQL中将多个字段拼接组合成新字段的常用方法。这些方法包括:使用CONCAT()函数使用||运算符使用GROUP_CONCAT()函数使用COALESCE()函数使用CASE...WHEN...END使用XMLPATH语法这些方法可用于简化数据操作和提高查询效率。字段类型、性能优化和空值处理是需要考虑的重要因素。
SQL多个字段拼接组合成新字段的常用方法
2024-04-02

JS数组操作大全对象数组根据某个相同的字段分组

这篇文章主要介绍了JS数组操作大全对象数组根据某个相同的字段分组,需要注意的是,在开发过程这种数组的处理函数,应当被编写到项目的公共工具函数库中全局调用,本文结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
2022-11-13

sql怎么对多个条件进行分组求和

在SQL中,可以使用GROUP BY子句来对多个条件进行分组,然后使用SUM函数对每个分组中的值进行求和。例如,假设有一个表格名为orders,其中包含以下列:order_date、customer_id和order_amount。如果要按
2023-08-08

Sequelize中用group by进行分组聚合查询

一、SQL与Sequelize中的分组查询1.1 SQL中的分组查询SQL查询中,通GROUP BY语名实现分组查询。GROUP BY子句要和聚合函数配合使用才能完成分组查询,在SELECT查询的字段中,如果没有使用聚合函数就必须出现在OR
2022-06-04

spark中如何使用groupByKey进行分组排序

今天小编给大家分享一下spark中如何使用groupByKey进行分组排序的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。任务
2023-07-05

SQL 如何查询每个分组都出现的字段值

数据分组以后,要找出在每个分组中都出现的某个字段值。 用SQL来解决这个问题并不容易! SQL缺少单纯的分组操作,只能返回各分组的统计值,而不能对各组的成员再进行更细的交、差、并等集合运算。要解决这种问题就需要多层嵌套子查询来变相地实现。 举个例子:查询每周都
SQL 如何查询每个分组都出现的字段值
2016-08-07

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录