当前位置: 首页 > news >正文

Java Stream 复杂场景排序与分组技术解析与示例代码

文章目录

    • 前言
    • 技术积累
    • 数据源准备
    • 复杂场景排序
      • 多级排序
      • 嵌套对象排序
      • 自定义排序逻辑
    • 复杂场景分组
      • 单字段分组
      • 多级分组
    • 多级分组与排序结合
      • 多级分组并按组内元素排序
      • 多级分组并按组内元素多级排序
    • 总结

前言

Java Stream API 自从 Java 8 引入以来,已经成为处理集合数据的强大工具。它提供了丰富的操作,包括过滤、映射、归约、排序和分组等。本文将深入探讨如何在复杂场景下使用 Java Stream 进行排序和分组,并通过示例代码展示各种排序和分组技巧。

在这里插入图片描述

技术积累

Java Stream API 提供了 sorted 方法,可以方便地对流中的元素进行排序。同时,Collectors.groupingBy 方法可以用于对流中的元素进行分组。在复杂场景下,如多级排序、自定义排序逻辑、处理嵌套对象、多级分组与排序结合等,使用这些方法时需要一些技巧和注意事项。本文将详细介绍这些复杂场景下的排序和分组方法。

数据源准备

首先新增一个person对象来作为演示数据源,内部提供一个初始化的方法。

/*** Person* @author senfel* @version 1.0* @date 2025/4/18 15:37*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {private String name;private String department;private int age;private double salary;private Address address;/*** init* @author senfel* @date 2025/4/18 15:39* @return java.util.List<com.example.ccedemo.stream.Person>*/public List<Person> init(){return  new ArrayList<Person>(Arrays.asList(new Person("ayan", "HR", 30, 10000, new Address(10088, "四川南充")),new Person("kiki", "Engineering", 25, 20000, new Address(10086, "四川成都")),new Person("tim", "HR", 28, 15000, new Address(10012, "重庆")),new Person( "senfel", "Engineering", 35, 15000, new Address(10086, "四川成都")),new Person("roy", "HR", 32, 12000, new Address(10010, "上海"))));}/*** getStreetNumber* @author senfel* @date 2025/4/18 18:51* @return java.lang.Integer*/public Integer getStreetNumber() {return this.getAddress().getStreetNumber();}@Data@AllArgsConstructor@NoArgsConstructorpublic static class Address {private int streetNumber;private String streetName;}
}

复杂场景排序

多级排序

是指根据多个字段对元素进行排序。可以使用 Comparator.comparing 和 thenComparing 方法实现。

/*** multilevelSortTest* @author senfel* @date 2025/4/18 15:58* @return void*/
@Test
public void  multilevelSortTest() {Person person = new Person();List<Person> personList = person.init();//先部门排序,再age排序personList = personList.stream().sorted(Comparator.comparing(Person::getDepartment).thenComparing(Person::getAge)).collect(Collectors.toList());/*Person(name=kiki, department=Engineering, age=25, salary=20000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))Person(name=senfel, department=Engineering, age=35, salary=15000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))Person(name=tim, department=HR, age=28, salary=15000.0, address=Person.Address(streetNumber=10012, streetName=重庆))Person(name=ayan, department=HR, age=30, salary=10000.0, address=Person.Address(streetNumber=10088, streetName=四川南充))Person(name=roy, department=HR, age=32, salary=12000.0, address=Person.Address(streetNumber=10010, streetName=上海))*/personList.forEach(System.out::println);
}

嵌套对象排序

当需要根据嵌套对象的字段进行排序时,可以使用 Comparator.comparing 方法并传递一个函数来提取嵌套对象的字段。

/*** nestedObjectSortTest* @author senfel* @date 2025/4/18 16:14* @return void*/
@Test
public void  nestedObjectSortTest() {Person person = new Person();List<Person> personList = person.init();//先根据部门排序,再根据嵌套街道号排序personList = personList.stream().sorted(Comparator.comparing(Person::getDepartment).thenComparing(Person::getAddress, Comparator.comparing(Person.Address::getStreetNumber))).collect(Collectors.toList());/*Person(name=kiki, department=Engineering, age=25, salary=20000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))Person(name=senfel, department=Engineering, age=35, salary=15000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))Person(name=roy, department=HR, age=32, salary=12000.0, address=Person.Address(streetNumber=10010, streetName=上海))Person(name=tim, department=HR, age=28, salary=15000.0, address=Person.Address(streetNumber=10012, streetName=重庆))Person(name=ayan, department=HR, age=30, salary=10000.0, address=Person.Address(streetNumber=10088, streetName=四川南充))*/personList.forEach(System.out::println);
}

自定义排序逻辑

有时需要根据复杂的逻辑进行排序,可以使用 Comparator.comparing 和自定义的比较器。

/*** customSortTest* @author senfel* @date 2025/4/18 17:55 * @return void*/
@Test
public void  customSortTest() {Person person = new Person();List<Person> personList = person.init();//工资一样比较部门,工资不一样比较街道号personList = personList.stream().sorted((e1,e2)->{if(e1.getSalary()==e2.getSalary()){return e1.getDepartment().compareTo(e2.getDepartment());}else{return Integer.compare(e1.getAddress().getStreetNumber(),e2.getAddress().getStreetNumber());}}).collect(Collectors.toList());/*Person(name=roy, department=HR, age=32, salary=12000.0, address=Person.Address(streetNumber=10010, streetName=上海))Person(name=senfel, department=Engineering, age=35, salary=15000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))Person(name=tim, department=HR, age=28, salary=15000.0, address=Person.Address(streetNumber=10012, streetName=重庆))Person(name=kiki, department=Engineering, age=25, salary=20000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))Person(name=ayan, department=HR, age=30, salary=10000.0, address=Person.Address(streetNumber=10088, streetName=四川南充))*/personList.forEach(System.out::println);
}

复杂场景分组

单字段分组

/*** singleFieldGroupTest* @author senfel* @date 2025/4/18 17:59 * @return void*/
@Test
public void  singleFieldGroupTest() {Person person = new Person();List<Person> personList = person.init();//通过部门分组Map<String, List<Person>> collect =personList.stream().collect(Collectors.groupingBy(Person::getDepartment));/*department:EngineeringPerson(name=kiki, department=Engineering, age=25, salary=20000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))Person(name=senfel, department=Engineering, age=35, salary=15000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))department:HRPerson(name=ayan, department=HR, age=30, salary=10000.0, address=Person.Address(streetNumber=10088, streetName=四川南充))Person(name=tim, department=HR, age=28, salary=15000.0, address=Person.Address(streetNumber=10012, streetName=重庆))Person(name=roy, department=HR, age=32, salary=12000.0, address=Person.Address(streetNumber=10010, streetName=上海))*/collect.forEach((k,v)->{System.out.println("department:"+k);v.forEach(System.out::println);});
}

多级分组

多级分组是指根据多个字段对元素进行分组。可以使用嵌套的 Collectors.groupingBy 方法实现。

/*** multiFieldGroupTest* @author senfel* @date 2025/4/18 18:41 * @return void*/
@Test
public void  multiFieldGroupTest() {Person person = new Person();List<Person> personList = person.init();//通过部门分组,内部再通过街道号分组Map<String, Map<Integer, List<Person>>> collect = personList.stream().collect(Collectors.groupingBy(Person::getDepartment, Collectors.groupingBy(Person::getAge)));/*department:Engineeringage:35Person(name=senfel, department=Engineering, age=35, salary=15000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))age:25Person(name=kiki, department=Engineering, age=25, salary=20000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))department:HRage:32Person(name=roy, department=HR, age=32, salary=12000.0, address=Person.Address(streetNumber=10010, streetName=上海))age:28Person(name=tim, department=HR, age=28, salary=15000.0, address=Person.Address(streetNumber=10012, streetName=重庆))age:30Person(name=ayan, department=HR, age=30, salary=10000.0, address=Person.Address(streetNumber=10088, streetName=四川南充))*/collect.forEach((k,v)->{System.out.println("department:"+k);v.forEach((k1,v1)-> {System.out.println("age:"+k1);v1.forEach(System.out::println);});});
}

多级分组与排序结合

在实际应用中,我们经常需要对分组后的数据进行排序。可以通过在分组后使用 Collectors.collectingAndThen 和 Collectors.toList 结合 sorted 方法来实现。

多级分组并按组内元素排序

/*** groupingAndSortTest* @author senfel* @date 2025/4/18 18:48 * @return void*/
@Test
public void  groupingAndSortTest() {Person person = new Person();List<Person> personList = person.init();//通过部门分组,内部再通过工资排序Map<String, List<Person>> collect = personList.stream().collect(Collectors.groupingBy(Person::getDepartment,Collectors.collectingAndThen(Collectors.toList(),list -> list.stream().sorted(Comparator.comparing(Person::getSalary)).collect(Collectors.toList()))));/*department:EngineeringPerson(name=senfel, department=Engineering, age=35, salary=15000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))Person(name=kiki, department=Engineering, age=25, salary=20000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))department:HRPerson(name=ayan, department=HR, age=30, salary=10000.0, address=Person.Address(streetNumber=10088, streetName=四川南充))Person(name=roy, department=HR, age=32, salary=12000.0, address=Person.Address(streetNumber=10010, streetName=上海))Person(name=tim, department=HR, age=28, salary=15000.0, address=Person.Address(streetNumber=10012, streetName=重庆))*/collect.forEach((k,v)->{System.out.println("department:"+k);v.forEach(System.out::println);});
}

多级分组并按组内元素多级排序

/*** multiLevelGroupingAndSortTest* @author senfel* @date 2025/4/18 18:56* @return void*/
@Test
public void  multiLevelGroupingAndSortTest() {Person person = new Person();List<Person> personList = person.init();personList.add(new Person("senfel2", "Engineering", 18, 15000, new Person.Address(10086, "四川成都")));//先根据部门分组,再根据街道号分组,再根据工资排序,最后根据年龄排序Map<String, Map<Integer, List<Person>>> collect = personList.stream().collect(Collectors.groupingBy(Person::getDepartment,Collectors.groupingBy(Person::getStreetNumber,Collectors.collectingAndThen(Collectors.toList(),list -> list.stream().sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).collect(Collectors.toList())))));/*department:EngineeringsheetNumber:10086Person(name=senfel2, department=Engineering, age=18, salary=15000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))Person(name=senfel, department=Engineering, age=35, salary=15000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))Person(name=kiki, department=Engineering, age=25, salary=20000.0, address=Person.Address(streetNumber=10086, streetName=四川成都))department:HRsheetNumber:10088Person(name=ayan, department=HR, age=30, salary=10000.0, address=Person.Address(streetNumber=10088, streetName=四川南充))sheetNumber:10010Person(name=roy, department=HR, age=32, salary=12000.0, address=Person.Address(streetNumber=10010, streetName=上海))sheetNumber:10012Person(name=tim, department=HR, age=28, salary=15000.0, address=Person.Address(streetNumber=10012, streetName=重庆))*/collect.forEach((k,v)->{System.out.println("department:"+k);v.forEach((k1,v1)-> {System.out.println("sheetNumber:"+k1);v1.forEach(System.out::println);});});
}

总结

Java Stream API 提供了强大的排序和分组功能,通过 sorted 方法可以方便地对流中的元素进行排序,通过 Collectors.groupingBy 方法可以对流中的元素进行分组。在复杂场景下,如多级排序、自定义排序逻辑、处理嵌套对象、多级分组与排序等,可以通过 Comparator.comparing 和 thenComparing 方法来实现多级排序,并通过Collectors.groupingBy来实现多级分组并按组内元素多级排序。

http://www.xdnf.cn/news/31951.html

相关文章:

  • 蓝桥杯 蜗牛 动态规划
  • 遨游科普:防爆平板是指什么?有哪些应用场景?
  • 使用vue2技术写了一个纯前端的静态网站商城-鲜花销售商城
  • javassist
  • Python concurrent.futures模块的ProcessPoolExecutor, ThreadPoolExecutor类介绍
  • 在 Node.js 中使用原生 `http` 模块,获取请求的各个部分:**请求行、请求头、请求体、请求路径、查询字符串** 等内容
  • Python爬虫实战:获取网易新闻数据
  • Windows系统安装`face_recognition`
  • 2. ubuntu20.04 和VS Code实现 ros的输出 (C++,Python)
  • DeepSeek与Napkin:信息可视化领域的创新利器
  • [matlab]南海地形眩晕图代码
  • Github 2025-04-19Rust开源项目日报 Top10
  • Prompt-Tuning 提示词微调
  • 机器学习核心算法全解析:从基础到进阶的 18 大算法模型
  • MySQL运维三部曲初级篇:从零开始打造稳定高效的数据库环境
  • 10软件测试需求分析案例-查询学习信息
  • 详讲Linux下进程等待
  • Go-zero框架修改模版进行handler统一响应封装
  • 手撕 简易HashMap
  • YOLO11改进-Backbone-使用MobileMamba替换YOLO backbone 提高检测精度
  • 在服务器上部署MinIO Server
  • JMeter实现UI自动化测试的完整方案
  • 配置管理与系统文档
  • MyImgConverter:图片批量处理工具
  • 智能提示语全周期优化系统:云原生架构设计与工程实践
  • LPDDR中读操作不存在Additive Latency(AL)的技术思考
  • opencv 最近邻插值法的原理
  • 集合框架(详解)
  • 手机投屏到电视方法
  • 从UDS协议学习ISO网络七层架构:汽车诊断网络协议的分层逻辑剖析