
本文深入探讨如何使用java 8 stream api对自定义对象(如`student`)进行多属性(如`name`, `age`, `city`)分组,并对其他数值属性(如`salary`, `incentive`)进行聚合求和。我们将通过创建自定义键类和累加器,结合`collectors.groupingby`与`collector.of`,构建一个高效且可读性强的解决方案,以解决传统方法在处理复杂聚合逻辑时的局限性。
在现代Java应用开发中,数据处理和转换是常见的任务。尤其是在处理集合数据时,经常需要根据对象的某些属性进行分组,并对其他属性执行聚合操作。例如,我们有一个Student列表,每个学生包含姓名、年龄、城市、薪资和奖金等信息。现在,我们需要根据学生的姓名、年龄和城市对学生进行分组,并将相同分组内学生的薪资和奖金进行累加,最终生成一个聚合后的学生列表。
假设我们有如下Student类:
public class Student {
private String name;
private int age;
private String city;
private double salary;
private double incentive;
// 全参构造函数
public Student(String name, int age, String city, double salary, double incentive) {
this.name = name;
this.age = age;
this.city = city;
this.salary = salary;
this.incentive = incentive;
}
// Getters
public String getName() { return name; }
public int getAge() { return age; }
public String getCity() { return city; }
public double getSalary() { return salary; }
public double getIncentive() { return incentive; }
// 为了方便打印结果,重写toString
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", city='" + city + '\'' +
", salary=" + salary +
", incentive=" + incentive +
'}';
}
}给定一个Student列表,例如:
Student("Raj", 10, "Pune", 10000, 100)
Student("Raj", 10, "Pune", 20000, 200)
Student("Raj", 20, "Pune", 10000, 100)
Student("Ram", 30, "Pune", 10000, 100)
Student("Ram", 30, "Pune", 30000, 300)
Student("Seema", 10, "Pune", 10000, 100)期望的输出是:
立即学习“Java免费学习笔记(深入)”;
Student("Raj", 10, "Pune", 30000, 300)
Student("Raj", 20, "Pune", 10000, 100)
Student("Ram", 30, "Pune", 40000, 400)
Student("Seema", 10, "Pune", 10000, 100)在尝试使用Collectors.toMap进行聚合时,我们可能会遇到以下问题:
为了解决这些问题,我们需要更灵活的策略,即引入自定义键对象和自定义累加器。
为了实现多属性分组和聚合,我们将采取以下步骤:
为了将name、age和city组合成一个唯一的键,我们需要一个自定义类。这个类必须正确地重写equals()和hashCode()方法,以确保在Map中作为键时能够正确地识别和比较。
import java.util.Objects; // 导入Objects类
public static class NameAgeCity {
private String name;
private int age;
private String city;
public NameAgeCity(String name, int age, String city) {
this.name = name;
this.age = age;
this.city = city;
}
// Getters
public String getName() { return name; }
public int getAge() { return age; }
public String getCity() { return city; }
// 静态工厂方法,方便从Student对象创建
public static NameAgeCity from(Student s) {
return new NameAgeCity(s.getName(), s.getAge(), s.getCity());
}
// 必须重写equals和hashCode以确保Map的正确行为
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NameAgeCity that = (NameAgeCity) o;
return age == that.age && Objects.equals(name, that.name) && Objects.equals(city, that.city);
}
@Override
public int hashCode() {
return Objects.hash(name, age, city);
}
@Override
public String toString() {
return "NameAgeCity{" +
"name='" + name + '\'' +
", age=" + age +
", city='" + city + '\'' +
'}';
}
}注意事项:
为了累加salary和incentive,我们需要一个可变的容器。这个容器不仅要存储累加后的值,还要能够处理单个Student的输入并与其他容器合并(在并行流中)。
import java.util.function.Consumer; // 导入Consumer接口
public static class AggregatedValues implements Consumer<Student> {
private String name;
private int age;
private String city;
private double salary;
private double incentive;
// 无参构造函数,用于Collector的supplier
public AggregatedValues() {
this.salary = 0.0;
this.incentive = 0.0;
}
// Getters
public String getName() { return name; }
public int getAge() { return age; }
public String getCity() { return city; }
public double getSalary() { return salary; }
public double getIncentive() { return incentive; }
// 实现Consumer接口的accept方法,用于累加单个Student对象
@Override
public void accept(Student s) {
// 首次接受Student时,初始化分组的name, age, city
// 假设同一个分组的所有Student这些属性都是相同的
if (name == null) name = s.getName();
if (age == 0) age = s.getAge(); // 注意:如果age可能为0,需要更严谨的判断
if (city == null) city = s.getCity();
this.salary += s.getSalary();
this.incentive += s.getIncentive();
}
// 合并方法,用于并行流将多个AggregatedValues实例合并
public AggregatedValues merge(AggregatedValues other) {
this.salary += other.salary;
this.incentive += other.incentive;
return this;
}
// 转换方法,将聚合结果转换回Student对象
public Student toStudent() {
return new Student(name, age, city, salary, incentive);
}
@Override
public String toString() {
return "AggregatedValues{" +
"name='" + name + '\'' +
", age=" + age +
", city='" + city + '\'' +
", salary=" + salary +
", incentive=" + incentive +
'}';
}
}注意事项:
现在,我们可以将上述自定义类集成到Stream操作中。我们将使用Collectors.groupingBy,它的第二个参数是一个“下游收集器”(downstream collector),这里我们将使用Collector.of来构建一个自定义的收集器。
Collector.of方法需要四个参数:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.function.Consumer; // 确保导入
public class StudentAggregator {
// ... (Student, NameAgeCity, AggregatedValues 类定义同上,确保是静态内部类或独立类) ...
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
// Java 8 兼容的添加元素方式
Collections.addAll(students,
new Student("Raj", 10, "Pune", 10000, 100),
new Student("Raj", 10, "Pune", 20000, 200),
new Student("Raj", 20, "Pune", 10000, 100),
new Student("Ram", 30, "Pune", 10000, 100),
new Student("Ram", 30, "Pune", 30000, 300),
new Student("Seema", 10, "Pune", 10000, 100)
);
// 执行分组和聚合
List<Student> aggregatedStudents = students.stream()
.collect(Collectors.groupingBy(
NameAgeCity::from, // keyMapper: 使用NameAgeCity::from作为键映射函数
Collectors.of( // downstream collector: 自定义收集器
AggregatedValues::new, // supplier: 提供新的AggregatedValues实例
AggregatedValues::accept, // accumulator: 将Student累加到AggregatedValues
AggregatedValues::merge, // combiner: 合并两个AggregatedValues实例
AggregatedValues::toStudent // finisher: 将AggregatedValues转换为Student
)
))
.values() // 获取Map中所有AggregatedValues(已转换为Student)的集合
.stream()
.collect(Collectors.toList()); // 收集到List
// 打印结果
aggregatedStudents.forEach(System.out::println);
}
// 嵌套类定义 (为了示例完整性,这里再次包含,实际代码可独立定义)
public static class Student {
private String name;
private int age;
private String city;
private double salary;
private double incentive;
public Student(String name, int age, String city, double salary, double incentive) {
this.name = name;
this.age = age;
this.city = city;
this.salary = salary;
this.incentive = incentive;
}
public String getName() { return name; }
public int getAge() { return age; }
public String getCity() { return city; }
public double getSalary() { return salary; }
public double getIncentive() { return incentive; }
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + ", city='" + city + '\'' + ", salary=" + salary + ", incentive=" + incentive + '}';
}
}
public static class NameAgeCity {
private String name;
private int age;
private String city;
public NameAgeCity(String name, int age, String city) {
this.name = name;
this.age = age;
this.city = city;
}
public String getName() { return name; }
public int getAge() { return age; }
public String getCity() { return city; }
public static NameAgeCity from(Student s) {
return new NameAgeCity(s.getName(), s.getAge(), s.getCity());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NameAgeCity that = (NameAgeCity) o;
return age == that.age && Objects.equals(name, that.name) && Objects.equals(city, that.city);
}
@Override
public int hashCode() {
return Objects.hash(name, age, city);
}
}
public static class AggregatedValues implements Consumer<Student> {
private String name;
private int age;
private String city;
private double salary;
private double incentive;
public AggregatedValues() {
this.salary = 0.0;
this.incentive = 0.0;
}
public String getName() { return name; }
public int getAge() { return age; }
public String getCity() { return city; }
public double getSalary() { return salary; }
public double getIncentive() { return incentive; }
@Override
public void accept(Student s) {
if (name == null) name = s.getName();
if (age == 0) age = s.getAge();
if (city == null) city = s.getCity();
salary += s.getSalary();
incentive += s.getIncentive();
}
public AggregatedValues merge(AggregatedValues other) {
salary += other.salary;
incentive += other.incentive;
return this;
}
public Student toStudent() {
return new Student(name, age, city, salary, incentive);
}
}
}输出结果:
Student{name='Raj', age=20, city='Pune', salary=10000.0, incentive=100.0}
Student{name='Raj', age=10, city='Pune', salary=30000.0, incentive=300.0}
Student{name='Ram', age=30, city='Pune', salary=40000.0, incentive=400.0}
Student{name='Seema', age=10, city='Pune', salary=10000.0, incentive=100.0}通过上述方法,我们成功地利用Java 8 Stream API实现了自定义对象的多属性分组与聚合。
核心要点:
这种模式不仅适用于学生数据,也适用于任何需要根据多个属性进行分组并聚合其他属性的自定义对象场景,是Java 8 Stream API高级用法中的一个重要技巧。
以上就是Java 8 Stream实现自定义对象多属性分组与聚合的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号