【Java】Stream 流 —— 解决 Java 集合运算

一、什么是 Stream 流

StreamJava 8 API 添加的一个新的抽象 , Stream 使用一种 类似用 SQL 语句从数据库查询数据 的直观方式提供一种对 Java 集合运算 的高阶抽象。

Stream API可以极大提高 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码

Stream 将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

二、Stream 流的使用

1. 生成流

(1)串行流 —— stream
(2)并行流 —— parallelStream
(3)示例

    public static void main(String[] args) {
        // 创建一个字符集合
        List<String> list = Arrays.asList(new String[]{"wanqing", "learns", "Java", ""});
        // 生成流
        // 方法 1  —— 创建串行流
        // 下面的代码实现 过滤出 list 中的空字符串,并且将结果转换为 list
        List<String> filtered = list.stream()
                                    .filter(string -> !string.isEmpty())
                                    .collect(Collectors.toList());
        System.out.println(filtered.size());
        // 方法 2 —— 创建并行流
        List<String> filtered2 = list.parallelStream()
                .filter(string -> !string.isEmpty())
                .collect(Collectors.toList());
    }

2. 流常用方法

(1) map

映射每个元素到对应的结果

示例1:

   		 // 创建一个字符集合
        List<String> list = Arrays.asList(new String[]{"wanqing", "learns", "Java", ""});
        // 下面的代码实现将 list 集合中所有字符串 string 映射(改变)为 string + string
        List<String> reduce = list.stream().map(string -> string + string).collect(Collectors.toList());
        // 测试输出 [wanqingwanqing, learnslearns, JavaJava, ]
        System.out.println(reduce);

示例2:

求取存放学生类的集合的所有学生年龄和

public class UseOfStream {

    public static void main(String[] args) {
        List<Student> list1 = new ArrayList<>();
        Student s1 = new Student(1, "a");
        Student s2 = new Student(2, "a");
        list1.add(s1);
        list1.add(s2);
        // 通过 mapToInt 将学生对象映射为 学生年龄age ,然后再使用 sum 求和 —— 注意:集合为空时这么使用会爆异常,要进行处理
        int sum2 = list1.stream().mapToInt(student->student.getAge()).sum();
        int max2 = list1.stream().mapToInt(student->student.getAge()).max().getAsInt();
        System.out.println("年龄和为:" + sum2);
        System.out.println("最大年龄:" + max2);
    }

    private static class Student{
        int age;
        String name;

        public Student(int age, String name) {
            this.age = age;
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

}

(2) filter

通过设置的条件过滤出元素

    	// 创建一个字符集合
        List<String> list = Arrays.asList(new String[]{"wanqing", "learns", "Java", ""});
        // 下面的代码实现 过滤出 list 中的空字符串,并且将结果转换为 list
        List<String> filtered = list.stream()
                                    .filter(string -> !string.isEmpty())
                                    .collect(Collectors.toList());

(3) sorted

用于对流进行排序,可以通过传入比较器自定义排序规则,也可以使用默认排序规则

关于比较器的内容请看:Java 比较器介绍

       List<String> sort = list.stream().sorted(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                // 自定义排序规则,这里用作示例
                return 0;
            }
        }).collect(Collectors.toList());

(4) 统计

使用产生统计结果的收集器对集合数据进行统计

        // 四、统计数据的使用
        List<Integer> listNums = Arrays.asList(new Integer[]{3, 7, 9, 2, 8});
        // 对 int 类型数据进行统计 - 得到整型统计类型类
        IntSummaryStatistics statistics = listNums.stream().mapToInt(num ->num).summaryStatistics();
        // 得到统计的各个结果
        int max = statistics.getMax();
        int min = statistics.getMin();
        long sum = statistics.getSum();
        double average = statistics.getAverage();

(5) reduce

reduceStream 的一个聚合方法,它把一个 Stream 的所有元素按照聚合函数聚合成一个结果

示例1:

单参数的 reduce 使用

		reduce(T 操作的基本数据类型,即泛型:: 操作名称)
        // reduce 的使用
        int max = listNums.stream().reduce(Integer:: max).orElse(-1);
        int sum = listNums.stream().reduce(Integer::sum).orElse(0);

示例2:

使用 reduce 对 map 映射后的数据进行处理

public class UseOfStream {

    public static void main(String[] args) {
        List<Student> list1 = new ArrayList<>();
        Student s1 = new Student(1, "a");
        Student s2 = new Student(2, "a");
        list1.add(s1);
        list1.add(s2);
        int sum3 = list1.stream().mapToInt(student->student.getAge()).reduce(Integer::sum).orElse(0);
        System.out.println("年龄和为:" + sum3);
    }
}
### 使用Java Stream API按字段对列表进行分组并收集到映射 为了实现基于对象某个字段的分组操作并将结果收集至`Map`,可以利用`Collectors.groupingBy()`方法。此方法允许指定分类函数作为参数来定义分组依据[^1]。 下面是一个具体的例子,假设有一个名为`Employee`的对象类,其中包含员工的名字(`name`)、部门(`dept`)以及工资(`salary`)属性: ```java import java.util.*; import java.util.stream.Collectors; class Employee { private String name; private String dept; private double salary; public Employee(String name, String dept, double salary) { this.name = name; this.dept = dept; this.salary = salary; } // Getters... @Override public String toString() { return "Employee{name='" + name + "', dept='" + dept + "', salary=" + salary + '}'; } } public class Main { public static void main(String[] args) { List<Employee> employees = Arrays.asList( new Employee("Alice", "HR", 7000), new Employee("Bob", "Engineering", 9000), new Employee("Charlie", "HR", 6500) ); Map<String, DoubleSummaryStatistics> result = employees.stream() .collect(Collectors.groupingBy(Employee::getDept, Collectors.summarizingDouble(Employee::getSalary))); System.out.println(result); } } ``` 上述代码展示了如何通过`groupingBy`按照部门名称分组,并且对于每一个分组计算该部门内所有成员薪资的相关统计信息(如平均数、总数等),这里使用的是`summarizingDouble`收集器。 如果目标是在分组的同时执行多种聚合运算,则可以通过嵌套收集器的方式完成。例如,在同一个分组下既求得某字段之和又获取其均值: ```java Map<String, Tuple<Double, Double>> complexResult = employees.stream() .collect(Collectors.groupingBy(Employee::getDept, Collectors.collectingAndThen( Collectors.teeing( Collectors.summingDouble(Employee::getSalary), Collectors.averagingDouble(Employee::getSalary), (total, avg) -> new Tuple<>(total, avg)) ) )); ``` 在这个复杂版本的例子中,引入了一个自定义的二元组(Tuple)用于存储两个不同类型的聚合结果——总和与平均值。需要注意的是,实际项目里可能需要创建这样的辅助类或采用其他形式的数据结构来容纳多维度的结果数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值