🌊 什么是Stream流?就像工厂流水线

想象一下你在一家手机工厂里,原材料从一端进入,经过不同的工序(组装、测试、包装),最后生产出成品手机。Java Stream流就是这样一条"数据流水线",让我们能够优雅地处理集合数据。

// 传统方式:像手工作坊一样一个一个处理
List<String> names = Arrays.asList("张三", "李四", "王五", "赵六");
List<String> result = new ArrayList<>();
for (String name : names) {
    if (name.length() == 2) {  // 筛选
        result.add(name.toUpperCase()); // 转换
    }
}

// Stream方式:像流水线一样流畅处理
List<String> streamResult = names.stream()
    .filter(name -> name.length() == 2)  // 筛选工序
    .map(String::toUpperCase)            // 转换工序
    .collect(Collectors.toList());       // 收集成品

🎯 Stream的核心概念

1. 三个阶段:准备→加工→收集

就像做菜一样:

  • 数据源(Source):准备食材 🥬
  • 中间操作(Intermediate Operations):切菜、调味、炒制 🔪
  • 终端操作(Terminal Operations):装盘上桌 🍽️
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// 找出所有偶数,乘以2,然后收集起来
List<Integer> processedNumbers = numbers.stream()  // 数据源:准备食材
    .filter(n -> n % 2 == 0)                      // 中间操作:筛选偶数
    .map(n -> n * 2)                              // 中间操作:乘以2
    .collect(Collectors.toList());                // 终端操作:装盘
    
System.out.println(processedNumbers); // [4, 8, 12, 16, 20]

2. 惰性求值:不到最后不干活

Stream就像一个"懒惰"的员工,只有在你真正需要结果的时候才开始工作。

Stream<String> lazyStream = names.stream()
    .filter(name -> {
        System.out.println("正在筛选: " + name);
        return name.startsWith("张");
    })
    .map(name -> {
        System.out.println("正在转换: " + name);
        return name.toUpperCase();
    });

System.out.println("Stream创建完毕,但还没有任何输出");

// 只有当调用终端操作时,才会真正执行
String result = lazyStream.findFirst().orElse("未找到");

🛠️ 常用操作大全

筛选操作:像挑选水果一样

List<Student> students = Arrays.asList(
    new Student("小明", 85, "男"),
    new Student("小红", 92, "女"),
    new Student("小刚", 78, "男"),
    new Student("小美", 96, "女")
);

// 筛选成绩优秀的女学生
List<Student> excellentGirls = students.stream()
    .filter(s -> s.getGender().equals("女"))      // 只要女生
    .filter(s -> s.getScore() >= 90)             // 成绩90分以上
    .collect(Collectors.toList());

// 去重:像去除重复的苹果
List<String> fruits = Arrays.asList("苹果", "香蕉", "苹果", "橙子", "香蕉");
List<String> uniqueFruits = fruits.stream()
    .distinct()
    .collect(Collectors.toList());

转换操作:像食材加工一样

// 单一转换:把名字转为大写
List<String> upperNames = students.stream()
    .map(Student::getName)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// 扁平化转换:像把多个包装盒里的糖果倒在一起
List<List<String>> nestedList = Arrays.asList(
    Arrays.asList("a", "b"),
    Arrays.asList("c", "d"),
    Arrays.asList("e", "f")
);

List<String> flatList = nestedList.stream()
    .flatMap(List::stream)  // 把嵌套的列表"压平"
    .collect(Collectors.toList());
// 结果:[a, b, c, d, e, f]

排序操作:像排队一样有序

// 按成绩从高到低排序
List<Student> sortedStudents = students.stream()
    .sorted((s1, s2) -> Integer.compare(s2.getScore(), s1.getScore()))
    .collect(Collectors.toList());

// 或者更简洁的方式
List<Student> sortedStudents2 = students.stream()
    .sorted(Comparator.comparing(Student::getScore).reversed())
    .collect(Collectors.toList());

🏆 终端操作:收获成果

查找操作:像在人群中找人

// 找到第一个满足条件的学生
Optional<Student> firstExcellent = students.stream()
    .filter(s -> s.getScore() >= 90)
    .findFirst();

// 检查是否所有学生都及格了
boolean allPassed = students.stream()
    .allMatch(s -> s.getScore() >= 60);

// 检查是否有学生满分
boolean hasFullMark = students.stream()
    .anyMatch(s -> s.getScore() == 100);

聚合操作:像统计数据一样

// 计算平均分
double averageScore = students.stream()
    .mapToInt(Student::getScore)
    .average()
    .orElse(0.0);

// 找出最高分
OptionalInt maxScore = students.stream()
    .mapToInt(Student::getScore)
    .max();

// 统计总分
int totalScore = students.stream()
    .mapToInt(Student::getScore)
    .sum();

// 计算人数
long count = students.stream()
    .filter(s -> s.getGender().equals("女"))
    .count();

🔥 进阶技巧:让你的代码更优雅

1. 分组:像分类整理一样

// 按性别分组
Map<String, List<Student>> groupByGender = students.stream()
    .collect(Collectors.groupingBy(Student::getGender));

// 按成绩等级分组
Map<String, List<Student>> groupByGrade = students.stream()
    .collect(Collectors.groupingBy(student -> {
        int score = student.getScore();
        if (score >= 90) return "优秀";
        else if (score >= 80) return "良好";
        else if (score >= 60) return "及格";
        else return "不及格";
    }));

2. 统计信息:一次性获取多种统计数据

IntSummaryStatistics stats = students.stream()
    .mapToInt(Student::getScore)
    .summaryStatistics();

System.out.println("平均分: " + stats.getAverage());
System.out.println("最高分: " + stats.getMax());
System.out.println("最低分: " + stats.getMin());
System.out.println("总分: " + stats.getSum());
System.out.println("人数: " + stats.getCount());

3. 自定义收集器:像定制加工方式

// 收集成绩前三名的学生姓名,用逗号分隔
String top3Names = students.stream()
    .sorted(Comparator.comparing(Student::getScore).reversed())
    .limit(3)
    .map(Student::getName)
    .collect(Collectors.joining(", ", "前三名: ", ""));

⚡ 性能优化技巧

1. 并行流:多线程加速

// 处理大量数据时使用并行流
List<Integer> largeList = IntStream.rangeClosed(1, 1000000)
    .boxed()
    .collect(Collectors.toList());

// 串行处理
long start = System.currentTimeMillis();
long sum1 = largeList.stream()
    .mapToLong(Integer::longValue)
    .sum();
long time1 = System.currentTimeMillis() - start;

// 并行处理
start = System.currentTimeMillis();
long sum2 = largeList.parallelStream()  // 注意这里用parallelStream
    .mapToLong(Integer::longValue)
    .sum();
long time2 = System.currentTimeMillis() - start;

System.out.println("串行耗时: " + time1 + "ms");
System.out.println("并行耗时: " + time2 + "ms");

2. 短路操作:能停就停

// 只要找到一个满足条件的就停止
boolean hasExcellent = students.stream()
    .peek(s -> System.out.println("检查: " + s.getName()))
    .anyMatch(s -> s.getScore() >= 95);  // 找到第一个就停止

// 限制处理数量
List<String> first3Names = students.stream()
    .map(Student::getName)
    .limit(3)  // 只处理前3个
    .collect(Collectors.toList());

🎨 实际应用场景

场景1:电商订单处理

public class OrderProcessor {
    public OrderSummary processOrders(List<Order> orders) {
        // 筛选本月的有效订单,按金额分组统计
        Map<String, Double> categoryTotal = orders.stream()
            .filter(order -> order.getStatus().equals("COMPLETED"))
            .filter(order -> isThisMonth(order.getCreateTime()))
            .collect(Collectors.groupingBy(
                Order::getCategory,
                Collectors.summingDouble(Order::getAmount)
            ));
        
        // 找出金额最大的订单
        Optional<Order> maxOrder = orders.stream()
            .max(Comparator.comparing(Order::getAmount));
        
        return new OrderSummary(categoryTotal, maxOrder.orElse(null));
    }
}

场景2:日志分析

public class LogAnalyzer {
    public void analyzeErrorLogs(List<LogEntry> logs) {
        // 统计各种错误类型的出现次数
        Map<String, Long> errorCounts = logs.stream()
            .filter(log -> log.getLevel().equals("ERROR"))
            .collect(Collectors.groupingBy(
                LogEntry::getErrorType,
                Collectors.counting()
            ));
        
        // 找出最近1小时的严重错误
        List<LogEntry> recentCritical = logs.stream()
            .filter(log -> log.getLevel().equals("ERROR"))
            .filter(log -> isWithinLastHour(log.getTimestamp()))
            .filter(log -> log.getMessage().contains("CRITICAL"))
            .sorted(Comparator.comparing(LogEntry::getTimestamp).reversed())
            .collect(Collectors.toList());
    }
}

🚨 常见陷阱与最佳实践

1. 不要过度使用

// ❌ 简单情况不需要Stream
List<String> names = Arrays.asList("张三", "李四");
// 过度设计
String first = names.stream().findFirst().orElse("");

// ✅ 直接使用更简单
String first = names.isEmpty() ? "" : names.get(0);

2. 注意并行流的使用场景

// ❌ 小数据集不适合并行流
List<Integer> smallList = Arrays.asList(1, 2, 3, 4, 5);
smallList.parallelStream().forEach(System.out::println); // 反而更慢

// ✅ 大数据集才适合并行流
List<Integer> largeList = IntStream.rangeClosed(1, 100000).boxed().collect(Collectors.toList());
largeList.parallelStream().map(this::heavyComputation).collect(Collectors.toList());

3. 合理使用Optional

// ✅ 优雅处理可能为空的结果
String result = students.stream()
    .filter(s -> s.getName().equals("张三"))
    .map(Student::getName)
    .findFirst()
    .orElse("未找到");

🎯 总结

Java Stream流就像一条高效的数据处理流水线,它让我们能够:

  1. 声明式编程:告诉程序"做什么"而不是"怎么做"
  2. 链式调用:像搭积木一样组合各种操作
  3. 函数式风格:代码更简洁、更容易理解
  4. 延迟执行:按需计算,提高效率

记住,Stream不是银弹,在合适的场景使用才能发挥其威力。当你需要对集合数据进行复杂的筛选、转换、聚合操作时,Stream就是你最好的伙伴!


希望这篇文章能帮助你更好地理解和使用Java Stream流。记住,最好的学习方式就是在实际项目中多多练习! 🚀

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐