Java8 Stream 按单一属性和多个属性实现集合分组
项目中已经通过处理逻辑获得了一个列表,后续需要根据列表的一个属性或多个属性对其分组,可以采用 Java8 的 Stream 实现。 举例说明如下,创建一个 Java Class,名为 Student,其有四个属性,分别是:学号(stuNo)、姓名(stuNm)、年龄(age)、性别(sex)。现有一个学生列表,分别根据年龄(单一属性)、年龄和性别(多属性)进行分组。 Student 类定义
项目中已经通过处理逻辑获得了一个列表,后续需要根据列表的一个属性或多个属性对其分组,可以采用 Java8 的 Stream 实现。
举例说明如下,创建一个 Java Class,名为 Student,其有四个属性,分别是:学号(stuNo)、姓名(stuNm)、年龄(age)、性别(sex)。现有一个学生列表,分别根据年龄(单一属性)、年龄和性别(多属性)进行分组。
Student 类定义如下:
package com.test.model;
import lombok.Getter;
@Getter
public class Student {
// 学号
private String stuNo;
// 姓名
private String stuNm;
// 年龄
private String age;
// 性别
private String sex;
public Student(String stuNo, String stuNm, String age, String sex) {
this.stuNo = stuNo;
this.stuNm = stuNm;
this.age = age;
this.sex = sex;
}
}
构造集合列表,用于后续测试:
package com.test.model;
import java.util.ArrayList;
import java.util.List;
public class ListStu {
// 构造测试数据,返回学生列表
public static List<Student> listStu() {
List<Student> stuList = new ArrayList<>();
stuList.add(new Student("2001", "段誉", "20", "男"));
stuList.add(new Student("2002", "乔峰", "20", "男"));
stuList.add(new Student("2003", "虚竹", "22", "男"));
stuList.add(new Student("2004", "张无忌", "22", "男"));
stuList.add(new Student("2005", "阿朱", "22", "女"));
stuList.add(new Student("2006", "王语嫣", "22", "女"));
return stuList;
}
}
一、根据单一属性(年龄:age)对学生列表进行分组并简单排序
package com.test.model;
import com.alibaba.fastjson.JSON;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<Student> stuList = ListStu.listStu();
// ------------- 将 stuList 按照 Student 的 age 属性进行分组,转为 map ------
Map<String, List<Student>> groupedMap =
stuList.stream().collect(Collectors.groupingBy(Student::getAge));
System.out.println("按年龄分组后的数据:");
System.out.println(JSON.toJSONString(groupedMap));
System.out.println("年龄为20的学生:");
System.out.println(JSON.toJSONString(groupedMap.get("20")));
// --------------- 将 stuList 按照 Student 的 age 属性进行分组,并按照 age 升序排序 --------------
TreeMap<String, List<Student>> treeMap = stuList.stream()
.collect(Collectors.groupingBy(Student::getAge, TreeMap::new, Collectors.toList()));
System.out.println("分组并按年龄升序排序后的 treeMap:");
System.out.println(JSON.toJSONString(treeMap));
}
}
执行结果如下:
按年龄分组后的数据:
{“22”:[{“age”:“22”,“sex”:“男”,“stuNm”:“虚竹”,“stuNo”:“2003”},{“age”:“22”,“sex”:“男”,“stuNm”:“张无忌”,“stuNo”:“2004”},{“age”:“22”,“sex”:“女”,“stuNm”:“阿朱”,“stuNo”:“2005”},{“age”:“22”,“sex”:“女”,“stuNm”:“王语嫣”,“stuNo”:“2006”}],“20”:[{“age”:“20”,“sex”:“男”,“stuNm”:“段誉”,“stuNo”:“2001”},{“age”:“20”,“sex”:“男”,“stuNm”:“乔峰”,“stuNo”:“2002”}]}
年龄为20的学生:
[{“age”:“20”,“sex”:“男”,“stuNm”:“段誉”,“stuNo”:“2001”},{“age”:“20”,“sex”:“男”,“stuNm”:“乔峰”,“stuNo”:“2002”}]
分组并按年龄升序排序后的 treeMap:
{“20”:[{“age”:“20”,“sex”:“男”,“stuNm”:“段誉”,“stuNo”:“2001”},{“age”:“20”,“sex”:“男”,“stuNm”:“乔峰”,“stuNo”:“2002”}],“22”:[{“age”:“22”,“sex”:“男”,“stuNm”:“虚竹”,“stuNo”:“2003”},{“age”:“22”,“sex”:“男”,“stuNm”:“张无忌”,“stuNo”:“2004”},{“age”:“22”,“sex”:“女”,“stuNm”:“阿朱”,“stuNo”:“2005”},{“age”:“22”,“sex”:“女”,“stuNm”:“王语嫣”,“stuNo”:“2006”}]}
二、根据多个属性(年龄和性别)对学生列表进行分组
方法1:嵌套调用 Java8 Collectors groupingBy 方法
package com.test.model;
import com.alibaba.fastjson.JSON;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<Student> stuList = ListStu.listStu();
// ------------------- 将 stuList 按照 Student 的 age 和 sex 属性进行分组 -------------------------
// 方法1:嵌套调用 Java8 Collectors groupingBy 方法
Map<String, Map<String, List<Student>>> map1 = stuList.stream()
.collect(Collectors.groupingBy(Student::getAge, Collectors.groupingBy(Student::getSex)));
System.out.println("按照年龄和性别分组后的数据:");
System.out.println(JSON.toJSONString(map1));
System.out.println("22岁的女学生:");
List<Student> list1 = map1.get("22").get("女");
System.out.println(JSON.toJSONString(list1));
}
}
执行结果如下:
按照年龄和性别分组后的数据:
{“22”:{“女”:[{“age”:“22”,“sex”:“女”,“stuNm”:“阿朱”,“stuNo”:“2005”},{“age”:“22”,“sex”:“女”,“stuNm”:“王语嫣”,“stuNo”:“2006”}],“男”:[{“age”:“22”,“sex”:“男”,“stuNm”:“虚竹”,“stuNo”:“2003”},{“age”:“22”,“sex”:“男”,“stuNm”:“张无忌”,“stuNo”:“2004”}]},“20”:{“男”:[{“age”:“20”,“sex”:“男”,“stuNm”:“段誉”,“stuNo”:“2001”},{“age”:“20”,“sex”:“男”,“stuNm”:“乔峰”,“stuNo”:“2002”}]}}
22岁的女学生:
[{“age”:“22”,“sex”:“女”,“stuNm”:“阿朱”,“stuNo”:“2005”},{“age”:“22”,“sex”:“女”,“stuNm”:“王语嫣”,“stuNo”:“2006”}]
方法2:多个属性拼接成一个组合属性
package com.test.model;
import com.alibaba.fastjson.JSON;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<Student> stuList = ListStu.listStu();
// ------------------- 将 stuList 按照 Student 的 age 和 sex 属性进行分组 -------------------------
// 方法2:多个属性拼接成一个组合属性
Map<String, List<Student>> map2 = stuList.stream().collect(Collectors.groupingBy(d -> fetchGroupKey(d)));
System.out.println("按照年龄和性别分组后的数据:");
System.out.println(JSON.toJSONString(map2));
System.out.println("22岁的女学生:");
System.out.println(JSON.toJSONString(map2.get("22_女")));
}
private static String fetchGroupKey(Student student) {
return student.getAge() + "_" + student.getSex();
}
}
执行结果如下:
按照年龄和性别分组后的数据:
{“20_男”:[{“age”:“20”,“sex”:“男”,“stuNm”:“段誉”,“stuNo”:“2001”},{“age”:“20”,“sex”:“男”,“stuNm”:“乔峰”,“stuNo”:“2002”}],“22_女”:[{“age”:“22”,“sex”:“女”,“stuNm”:“阿朱”,“stuNo”:“2005”},{“age”:“22”,“sex”:“女”,“stuNm”:“王语嫣”,“stuNo”:“2006”}],“22_男”:[{“age”:“22”,“sex”:“男”,“stuNm”:“虚竹”,“stuNo”:“2003”},{“age”:“22”,“sex”:“男”,“stuNm”:“张无忌”,“stuNo”:“2004”}]}
22岁的女学生:
[{“age”:“22”,“sex”:“女”,“stuNm”:“阿朱”,“stuNo”:“2005”},{“age”:“22”,“sex”:“女”,“stuNm”:“王语嫣”,“stuNo”:“2006”}]
方法3:在 Student 类中构造静态内部类,静态内部类的成员变量即为分组对应的多个属性
构造好静态内部类的 Student 类如下:
package com.test.model;
import lombok.Getter;
@Getter
public class Student {
// 学号
private String stuNo;
// 姓名
private String stuNm;
// 年龄
private String age;
// 性别
private String sex;
public Student(String stuNo, String stuNm, String age, String sex) {
this.stuNo = stuNo;
this.stuNm = stuNm;
this.age = age;
this.sex = sex;
}
// 静态内部类,含 age 和 sex 属性
public static class AgeSex {
public String age;
public String sex;
public AgeSex(String age, String sex) {
this.age = age;
this.sex = sex;
}
// 注意:因为 ageSex 对象会作为 Map 的 key,所以需要重写 equals 和 hashCode 方法,
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AgeSex)) return false;
AgeSex ageSex = (AgeSex) o;
if (age != null ? !age.equals(ageSex.age) : ageSex.age != null) return false;
return sex != null ? sex.equals(ageSex.sex) : ageSex.sex == null;
}
@Override
public int hashCode() {
int result = age != null ? age.hashCode() : 0;
result = 31 * result + (sex != null ? sex.hashCode() : 0);
return result;
}
}
public AgeSex getAgeSex() {
return new AgeSex(age, sex);
}
}
或者用 Lombok 的 @Data 注解为 Java 类自动生成 equals() 和 hashCode() 方法,示例如下:
package com.test.model;
import lombok.Data;
@Data
public class Student {
// 学号
private String stuNo;
// 姓名
private String stuNm;
// 年龄
private String age;
// 性别
private String sex;
public Student(String stuNo, String stuNm, String age, String sex) {
this.stuNo = stuNo;
this.stuNm = stuNm;
this.age = age;
this.sex = sex;
}
// 静态内部类,含 age 和 sex 属性
@Data
public static class AgeSex {
public String age;
public String sex;
public AgeSex(String age, String sex) {
this.age = age;
this.sex = sex;
}
}
public AgeSex getAgeSex() {
return new AgeSex(age, sex);
}
}
分组处理方法如下:
package com.test.model;
import com.alibaba.fastjson.JSON;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<Student> stuList = ListStu.listStu();
// ------------------- 将 stuList 按照 Student 的 age 和 sex 属性进行分组 ---------------------
// 方法3:在 Student 类里构造静态内部类(成员变量即分组对应的多个属性:age 和 sex)
Map<Student.AgeSex, List<Student>> map3 = stuList.stream()
.collect(Collectors.groupingBy(Student::getAgeSex));
System.out.println("按照年龄和性别分组后的数据:");
System.out.println(JSON.toJSONString(map3));
System.out.println("22岁的女学生:");
List<Student> list3 = map3.get(new Student.AgeSex("22", "女"));
System.out.println(JSON.toJSONString(list3));
}
}
执行结果如下所示:
按照年龄和性别分组后的数据:
{{“age”:“22”,“sex”:“男”}:[{“age”:“22”,“ageSex”:{“age”:“22”,“sex”:“男”},“sex”:“男”,“stuNm”:“虚竹”,“stuNo”:“2003”},{“age”:“22”,“ageSex”:{“age”:“22”,“sex”:“男”},“sex”:“男”,“stuNm”:“张无忌”,“stuNo”:“2004”}],{“age”:“20”,“sex”:“男”}:[{“age”:“20”,“ageSex”:{“age”:“20”,“sex”:“男”},“sex”:“男”,“stuNm”:“段誉”,“stuNo”:“2001”},{“age”:“20”,“ageSex”:{“age”:“20”,“sex”:“男”},“sex”:“男”,“stuNm”:“乔峰”,“stuNo”:“2002”}],{“age”:“22”,“sex”:“女”}:[{“age”:“22”,“ageSex”:{“age”:“22”,“sex”:“女”},“sex”:“女”,“stuNm”:“阿朱”,“stuNo”:“2005”},{“age”:“22”,“ageSex”:{“age”:“22”,“sex”:“女”},“sex”:“女”,“stuNm”:“王语嫣”,“stuNo”:“2006”}]}
22岁的女学生:
[{“age”:“22”,“ageSex”:{“age”:“22”,“sex”:“女”},“sex”:“女”,“stuNm”:“阿朱”,“stuNo”:“2005”},{“age”:“22”,“ageSex”:{“age”:“22”,“sex”:“女”},“sex”:“女”,“stuNm”:“王语嫣”,“stuNo”:“2006”}]
以上是用 Java8 Stream 对集合进行分组的几种方法,参考了以下链接:
https://stackoverflow.com/questions/28342814/group-by-multiple-field-names-in-java-8#
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)