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#

Java 8中的Stream API提供了一种方便的方法来对集合进行操作。其中之一是分组和提取属性。下面是一个例子,假设我们有一个Person类,其中包含姓名和年龄两个属性。我们想要将这些人按照年龄分组,并提取他们的姓名。可以使用Stream的groupingBy和mapping方法来实现这个目标。代码如下: ``` public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } } public class StreamTest { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Alice", 20), new Person("Bob", 30), new Person("Charlie", 20), new Person("David", 25) ); Map<Integer, List<String>> result = people.stream() .collect(Collectors.groupingBy(Person::getAge, Collectors.mapping(Person::getName, Collectors.toList()))); System.out.println(result); } } ``` 在这个例子中,我们首先创建了一个Person类,然后创建了一个包含四个人的列表。接下来,我们使用Stream的groupingBy方法按照年龄对人进行分组。在这个方法中,我们使用Person::getAge作为分组依据。然后,我们使用mapping方法来提取每个人的姓名,并将结果收集到一个列表中。最后,我们将结果存储在一个Map中,并将年龄作为键,姓名列表作为值。输出结果为:{20=[Alice, Charlie], 25=[David], 30=[Bob]}。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值