跳过正文
Java--Stream流
  1. TP` Blog/

Java--Stream流

·6 分钟· loading ·
龙城飞将
作者
龙城飞将
最美的是月亮🌙……
目录
Java学习之旅 - 这篇文章属于一个选集。
§ 7: 本文

Stream 流是 JDK 8 引入的一套全新 API(java.util.stream.),可以用来操作集合或者数组的数据。它结合函数式编程风格(Lambda 表达式),让数据处理变得更加优雅和高效。


💡 什么是 Stream 流
#

Stream 流是 Java 8 中处理集合数据的一种声明式方式。它不是数据结构,不存储数据,而是一个数据处理管道,可以对数据源(集合、数组等)进行复杂的查找、过滤、映射、聚合等操作。

核心特点
#

  • 不存储数据:Stream 不会存储元素,它只是传递数据源的视图
  • 函数式编程:操作通过 Lambda 表达式实现,代码简洁
  • 惰性执行:中间操作不会立即执行,只有终止操作触发时才会计算
  • 可消费性:Stream 只能使用一次,操作后不可重复使用
  • 支持并行:可轻松切换为并行流,充分利用多核 CPU

🎯 Stream 的核心优势
#

优势 传统方式 Stream 方式
代码简洁性 需要编写大量循环、判断语句 链式调用,一行代码完成复杂操作
可读性 逻辑分散在多个代码块中 声明式编程,业务意图清晰明了
性能优化 手动优化困难 内部迭代优化 + 惰性求值 + 短路操作
并行处理 需要手动管理线程 一个方法调用即可切换并行模式
函数式组合 难以复用和组合操作 可灵活组合各种操作符

🔧 Stream 的主要功能
#

1️⃣ 创建 Stream
#

创建方式 说明
[collection.stream](http://collection.stream)() 从集合创建串行流
collection.parallelStream() 从集合创建并行流
[Arrays.stream](http://Arrays.stream)(array) 从数组创建流
Stream.of(T... values) 从指定值创建流
Stream.generate(Supplier) 生成无限流(需要 limit 限制)
Stream.iterate(seed, UnaryOperator) 迭代生成流
Files.lines(Path) 从文件读取行创建流

2️⃣ 中间操作(Intermediate Operations)
#

这些操作返回新的 Stream,支持链式调用,惰性执行

操作 作用 示例
filter(Predicate) 过滤符合条件的元素 stream.filter(x -> x > 10)
map(Function) 将元素映射为其他形式 [stream.map](http://stream.map)(String::toUpperCase)
flatMap(Function) 将每个元素映射为流,然后扁平化 stream.flatMap(List::stream)
distinct() 去除重复元素 stream.distinct()
sorted() 自然排序 stream.sorted()
sorted(Comparator) 自定义排序 stream.sorted(Comparator.reverseOrder())
peek(Consumer) 对每个元素执行操作(调试用) stream.peek(System.out::println)
limit(long) 限制流的大小 stream.limit(5)
skip(long) 跳过前 n 个元素 stream.skip(3)

3️⃣ 终止操作(Terminal Operations)
#

触发流的计算,返回非 Stream 结果:

操作 作用 示例
forEach(Consumer) 遍历每个元素 stream.forEach(System.out::println)
collect(Collector) 收集结果到集合 stream.collect(Collectors.toList())
toArray() 转换为数组 stream.toArray(String[]::new)
reduce(BinaryOperator) 将流中元素反复结合,得到一个值 stream.reduce((a, b) -> a + b)
count() 返回流中元素个数 stream.count()
max(Comparator) 返回最大值 stream.max(Comparator.naturalOrder())
min(Comparator) 返回最小值 stream.min(Comparator.naturalOrder())
anyMatch(Predicate) 是否有元素匹配 stream.anyMatch(x -> x > 10)
allMatch(Predicate) 是否所有元素匹配 stream.allMatch(x -> x > 0)
noneMatch(Predicate) 是否没有元素匹配 stream.noneMatch(x -> x < 0)
findFirst() 返回第一个元素 stream.findFirst()
findAny() 返回任意一个元素 stream.findAny()

📝 Stream 的使用流程
#

创建流中间操作(可多次链式调用)终止操作

数据源 → stream() → filter() → map() → sorted() → collect()
   ↓         ↓          ↓         ↓         ↓           ↓
  集合    创建流    过滤数据   转换数据   排序数据    收集结果

💻 代码开发实例
#

🔹 实例 1:基础过滤与映射
#

需求:从员工列表中筛选出工资大于 8000 的员工姓名

import java.util.*;
import [java.util.stream](http://java.util.stream).*;

public class StreamExample1 {
    public static void main(String[] args) {
        // 创建员工列表
        List<Employee> employees = Arrays.asList(
            new Employee("张三", 7500),
            new Employee("李四", 9200),
            new Employee("王五", 8800),
            new Employee("赵六", 6500),
            new Employee("钱七", 10000)
        );
        
        // 使用 Stream 筛选并获取姓名
        List<String> highSalaryNames = [employees.stream](http://employees.stream)()
            .filter(emp -> emp.getSalary() > 8000)  // 过滤
            .map(Employee::getName)                  // 映射
            .collect(Collectors.toList());           // 收集
        
        System.out.println("高薪员工:" + highSalaryNames);
        // 输出:高薪员工:[李四, 王五, 钱七]
    }
}

class Employee {
    private String name;
    private double salary;
    
    public Employee(String name, double salary) {
        [this.name](http://this.name) = name;
        this.salary = salary;
    }
    
    public String getName() { return name; }
    public double getSalary() { return salary; }
}

🔹 实例 2:排序与限制
#

需求:获取成绩前 3 名的学生

import java.util.*;
import [java.util.stream](http://java.util.stream).*;

public class StreamExample2 {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
            new Student("小明", 85),
            new Student("小红", 92),
            new Student("小刚", 78),
            new Student("小丽", 95),
            new Student("小华", 88)
        );
        
        // 按成绩降序排序,取前 3 名
        List<Student> top3 = [students.stream](http://students.stream)()
            .sorted(Comparator.comparing(Student::getScore).reversed())
            .limit(3)
            .collect(Collectors.toList());
        
        System.out.println("前三名:");
        top3.forEach(s -> System.out.println(s.getName() + ": " + s.getScore()));
        /* 输出:
        前三名:
        小丽: 95
        小红: 92
        小华: 88
        */
    }
}

class Student {
    private String name;
    private int score;
    
    public Student(String name, int score) {
        [this.name](http://this.name) = name;
        this.score = score;
    }
    
    public String getName() { return name; }
    public int getScore() { return score; }
}

🔹 实例 3:分组与统计
#

需求:按部门分组统计员工人数和平均工资

import java.util.*;
import [java.util.stream](http://java.util.stream).*;

public class StreamExample3 {
    public static void main(String[] args) {
        List<Employee2> employees = Arrays.asList(
            new Employee2("张三", "技术部", 8000),
            new Employee2("李四", "技术部", 9500),
            new Employee2("王五", "销售部", 7000),
            new Employee2("赵六", "销售部", 7500),
            new Employee2("钱七", "技术部", 11000)
        );
        
        // 按部门分组
        Map<String, List<Employee2>> byDept = [employees.stream](http://employees.stream)()
            .collect(Collectors.groupingBy(Employee2::getDepartment));
        
        System.out.println("=== 按部门分组 ===");
        byDept.forEach((dept, empList) -> {
            System.out.println(dept + ": " + empList.size() + "人");
        });
        
        // 计算各部门平均工资
        Map<String, Double> avgSalaryByDept = [employees.stream](http://employees.stream)()
            .collect(Collectors.groupingBy(
                Employee2::getDepartment,
                Collectors.averagingDouble(Employee2::getSalary)
            ));
        
        System.out.println("\n=== 各部门平均工资 ===");
        avgSalaryByDept.forEach((dept, avg) -> {
            System.out.printf("%s: %.2f元\n", dept, avg);
        });
        
        /* 输出:
        === 按部门分组 ===
        技术部: 3人
        销售部: 2人
        
        === 各部门平均工资 ===
        技术部: 9500.00元
        销售部: 7250.00元
        */
    }
}

class Employee2 {
    private String name;
    private String department;
    private double salary;
    
    public Employee2(String name, String department, double salary) {
        [this.name](http://this.name) = name;
        this.department = department;
        this.salary = salary;
    }
    
    public String getName() { return name; }
    public String getDepartment() { return department; }
    public double getSalary() { return salary; }
}

🔹 实例 4:flatMap 扁平化处理
#

需求:将多个班级的学生合并为一个列表

import java.util.*;
import [java.util.stream](http://java.util.stream).*;

public class StreamExample4 {
    public static void main(String[] args) {
        List<String> class1 = Arrays.asList("张三", "李四", "王五");
        List<String> class2 = Arrays.asList("赵六", "钱七");
        List<String> class3 = Arrays.asList("孙八", "周九", "吴十");
        
        List<List<String>> school = Arrays.asList(class1, class2, class3);
        
        // 使用 flatMap 扁平化
        List<String> allStudents = [school.stream](http://school.stream)()
            .flatMap(List::stream)
            .collect(Collectors.toList());
        
        System.out.println("所有学生:" + allStudents);
        // 输出:所有学生:[张三, 李四, 王五, 赵六, 钱七, 孙八, 周九, 吴十]
        
        System.out.println("学生总数:" + allStudents.size());
        // 输出:学生总数:8
    }
}

🔹 实例 5:去重与统计
#

需求:统计订单中不同商品的数量

import java.util.*;
import [java.util.stream](http://java.util.stream).*;

public class StreamExample5 {
    public static void main(String[] args) {
        List<String> orders = Arrays.asList(
            "手机", "电脑", "手机", "平板", 
            "电脑", "手机", "耳机", "平板"
        );
        
        // 去重并统计
        long distinctCount = [orders.stream](http://orders.stream)()
            .distinct()
            .count();
        
        System.out.println("不同商品种类:" + distinctCount);
        // 输出:不同商品种类:4
        
        // 统计每种商品的数量
        Map<String, Long> productCount = [orders.stream](http://orders.stream)()
            .collect(Collectors.groupingBy(
                product -> product,
                Collectors.counting()
            ));
        
        System.out.println("\n各商品订单数:");
        productCount.forEach((product, count) -> {
            System.out.println(product + ": " + count + "单");
        });
        
        /* 输出:
        各商品订单数:
        平板: 2单
        电脑: 2单
        耳机: 1单
        手机: 3单
        */
    }
}

🔹 实例 6:reduce 聚合操作
#

需求:计算购物车总价

import java.util.*;
import [java.util.stream](http://java.util.stream).*;

public class StreamExample6 {
    public static void main(String[] args) {
        List<Product> cart = Arrays.asList(
            new Product("手机", 2999.0),
            new Product("耳机", 299.0),
            new Product("充电器", 99.0),
            new Product("数据线", 39.0)
        );
        
        // 计算总价
        double totalPrice = [cart.stream](http://cart.stream)()
            .map(Product::getPrice)
            .reduce(0.0, Double::sum);
        
        System.out.printf("购物车总价:%.2f元\n", totalPrice);
        // 输出:购物车总价:3436.00元
        
        // 使用 sum() 方法(更简洁)
        double total2 = [cart.stream](http://cart.stream)()
            .mapToDouble(Product::getPrice)
            .sum();
        
        System.out.printf("购物车总价:%.2f元\n", total2);
    }
}

class Product {
    private String name;
    private double price;
    
    public Product(String name, double price) {
        [this.name](http://this.name) = name;
        this.price = price;
    }
    
    public String getName() { return name; }
    public double getPrice() { return price; }
}

🔹 实例 7:并行流提升性能
#

需求:计算大量数据的总和

import java.util.*;
import [java.util.stream](http://java.util.stream).*;

public class StreamExample7 {
    public static void main(String[] args) {
        // 生成 1000 万个随机数
        List<Integer> numbers = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < 10_000_000; i++) {
            numbers.add(random.nextInt(100));
        }
        
        // 串行流计算
        long start1 = System.currentTimeMillis();
        long sum1 = [numbers.stream](http://numbers.stream)()
            .mapToLong(Integer::longValue)
            .sum();
        long time1 = System.currentTimeMillis() - start1;
        
        // 并行流计算
        long start2 = System.currentTimeMillis();
        long sum2 = numbers.parallelStream()
            .mapToLong(Integer::longValue)
            .sum();
        long time2 = System.currentTimeMillis() - start2;
        
        System.out.println("串行流结果:" + sum1 + ",耗时:" + time1 + "ms");
        System.out.println("并行流结果:" + sum2 + ",耗时:" + time2 + "ms");
        System.out.println("性能提升:" + (time1 - time2) + "ms");
        
        /* 输出示例:
        串行流结果:495059712,耗时:156ms
        并行流结果:495059712,耗时:52ms
        性能提升:104ms
        */
    }
}

⚡ 性能优化建议
#

何时使用并行流?

适合使用并行流的场景:

  • 数据量大(通常 > 10000)
  • 操作计算密集(复杂的 map、filter 等)
  • 无状态操作(操作之间相互独立)

不适合使用并行流的场景:

  • 数据量小(并行开销 > 性能收益)
  • 操作依赖顺序(如有状态的 sorted、distinct)
  • 涉及 I/O 操作(如数据库、文件读写)
  • 使用 ArrayList 等易分割的数据结构效果更好,LinkedList 等链表结构不适合

📚 常用 Collectors 收集器
#

收集器 说明
Collectors.toList() 收集为 List
Collectors.toSet() 收集为 Set(自动去重)
Collectors.toMap() 收集为 Map
Collectors.joining() 连接字符串
Collectors.groupingBy() 分组
Collectors.partitioningBy() 分区(true/false 两组)
Collectors.counting() 计数
Collectors.summingInt() 求和
Collectors.averagingDouble() 求平均值
Collectors.maxBy() 求最大值
Collectors.minBy() 求最小值

🎓 学习建议与最佳实践
#

✨ Stream 使用最佳实践

  1. 优先使用 Stream:对于集合操作,Stream 通常比传统循环更清晰
  2. 避免过长的链式调用:超过 5 个操作考虑拆分或添加注释
  3. 注意惰性求值:中间操作不会立即执行,调试时使用 peek() 查看中间结果
  4. 合理使用并行流:不是所有场景都适合并行,小数据集反而会降低性能
  5. 避免副作用:Lambda 表达式中不要修改外部变量
  6. Stream 只能使用一次:需要多次遍历时重新创建 Stream
  7. 使用方法引用:如 String::toUpperCase 比 Lambda 更简洁
  8. 选择合适的终止操作forEach 用于遍历,collect 用于收集结果

📌 总结
#

Stream API 是 Java 8 引入的革命性特性,它将函数式编程的思想带入 Java,让数据处理变得更加优雅、简洁、高效

核心要点:

  • 🎯 声明式编程:关注"做什么"而非"怎么做"
  • 性能优化:惰性求值 + 短路操作 + 并行流
  • 🔗 链式操作:流畅的 API 设计,可读性强
  • 🛠️ 丰富的操作符:过滤、映射、排序、分组、聚合一应俱全

掌握 Stream API 是现代 Java 开发的必备技能,它能大幅提升代码质量和开发效率!

Java学习之旅 - 这篇文章属于一个选集。
§ 7: 本文