Java8 Stream 按单一属性和多个属性实现集合分组

  项目中已经通过处理逻辑获得了一个列表,后续需要根据列表的一个属性或多个属性对其分组,可以采用 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#

猜你喜欢

转载自blog.csdn.net/piaoranyuji/article/details/107780336