Java 8 Stream 流全面使用教程 - 完整版

目录

​​​​​

Stream 基础概念与特性

什么是Stream?

Stream的核心特性

Stream操作的分类

Stream 创建方式大全

1. 从集合创建Stream

2. 从数组创建Stream

3. 生成Stream的各种方法

4. 从文件和其他资源创建Stream

中间操作详解

1. filter() - 过滤操作

2. map() - 映射转换操作

3. flatMap() - 扁平化映射操作

4. distinct(), sorted(), peek(), limit(), skip()

终端操作详解

1. collect() - 收集操作

2. reduce() - 归约操作

3. 匹配操作 (anyMatch, allMatch, noneMatch)

4. 查找操作 (findFirst, findAny)

实际开发中的常用模式

1. 查找并提取属性的模式

2. 数据转换和映射模式

3. 过滤和验证模式

收集器Collectors详解

1. 基础收集器

2. 分组收集器 groupingBy

3. 分区收集器 partitioningBy

4. 字符串收集器和数值收集器

并行流与性能优化

1. 并行流基础


Stream 基础概念与特性

什么是Stream?

Stream是Java 8引入的函数式编程API,用于对集合数据进行声明式处理。

// 传统方式:命令式编程
List<String> names = new ArrayList<>();
for (Person person : people) {
    if (person.getAge() > 18) {
        names.add(person.getName().toUpperCase());
    }
}

// Stream方式:声明式编程
List<String> names = people.stream()
    .filter(person -> person.getAge() > 18)    // 过滤成年人
    .map(person -> person.getName().toUpperCase())  // 转换为大写姓名
    .collect(Collectors.toList());             // 收集为列表

Stream的核心特性

/**
 * Stream的四大特性演示
 */
public class StreamCharacteristics {
    
    public static void demonstrateCharacteristics() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        // 1. 不存储数据 - Stream不是数据结构,不存储元素
        Stream<Integer> stream = numbers.stream();  // 只是数据的视图
        
        // 2. 函数式编程 - 不修改原数据源
        List<Integer> doubled = numbers.stream()
            .map(n -> n * 2)                       // 映射操作不改变原集合
            .collect(Collectors.toList());
        System.out.println("原集合: " + numbers);      // [1, 2, 3, 4, 5]
        System.out.println("新集合: " + doubled);      // [2, 4, 6, 8, 10]
        
        // 3. 惰性求值 - 中间操作延迟执行,只有终端操作才触发计算
        Stream<Integer> lazyStream = numbers.stream()
            .filter(n -> {
                System.out.println("过滤: " + n);  // 这里不会执行
                return n % 2 == 0;
            })
            .map(n -> {
                System.out.println("映射: " + n);  // 这里也不会执行
                return n * 2;
            });
        // 上面的代码不会有任何输出,因为没有终端操作
        
        System.out.println("开始执行终端操作:");
        List<Integer> result = lazyStream.collect(Collectors.toList()); // 现在才执行
        
        // 4. 可消费性 - Stream只能使用一次
        Stream<Integer> onceStream = numbers.stream();
        onceStream.count();  // 第一次使用
        // onceStream.findFirst();  // 错误!Stream已经被消费了
    }
}

Stream操作的分类

/**
 * Stream操作分为中间操作和终端操作
 */
public class StreamOperationTypes {
    
    // 中间操作 (Intermediate Operations) - 返回Stream,支持链式调用
    public static void intermediateOperations() {
        List<String> words = Arrays.asList("apple", "banana", "cherry");
        
        Stream<String> processed = words.stream()
            .filter(word -> word.length() > 5)     // 中间操作:过滤
            .map(String::toUpperCase)              // 中间操作:映射
            .distinct()                            // 中间操作:去重
            .sorted();                             // 中间操作:排序
        // 注意:上面的代码不会执行任何操作,因为没有终端操作
    }
    
    // 终端操作 (Terminal Operations) - 产生结果,触发Stream执行
    public static void terminalOperations() {
        List<String> words = Arrays.asList("apple", "banana", "cherry");
        
        // 不同的终端操作
        List<String> collected = words.stream()
            .filter(word -> word.length() > 5)
            .collect(Collectors.toList());         // 终端操作:收集
        
        long count = words.stream()
            .filter(word -> word.length() > 5)
            .count();                              // 终端操作:计数
        
        boolean anyMatch = words.stream()
            .anyMatch(word -> word.startsWith("a")); // 终端操作:匹配
        
        words.stream()
            .filter(word -> word.length() > 5)
            .forEach(System.out::println);         // 终端操作:遍历
    }
}

Stream 创建方式大全

1. 从集合创建Stream

/**
 * 从各种集合类型创建Stream
 */
public class CollectionToStream {
    
    public static void createFromCollections() {
        // 从List创建
        List<String> list = Arrays.asList("apple", "banana", "cherry");
        Stream<String> listStream = list.stream();           // 顺序流
        Stream<String> parallelStream = list.parallelStream(); // 并行流
        
        // 从Set创建
        Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
        Stream<Integer> setStream = set.stream();
        
        // 从Map创建不同类型的流
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 5);
        map.put("banana", 6);
        map.put("cherry", 6);
        
        Stream<String> keyStream = map.keySet().stream();      // 键流
        Stream<Integer> valueStream = map.values().stream();   // 值流
        Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream(); // 键值对流
        
        // 从Map中提取特定信息
        List<String> fruitsWithSixLetters = map.entrySet().stream()
            .filter(entry -> entry.getValue() == 6)           // 过滤长度为6的水果
            .map(Map.Entry::getKey)                           // 提取水果名称
            .collect(Collectors.toList());
        System.out.println("六个字母的水果: " + fruitsWithSixLetters);
    }
}

2. 从数组创建Stream

/**
 * 从数组创建各种类型的Stream
 */
public class ArrayToStream {
    
    public static void createFromArrays() {
        // 从对象数组创建
        String[] stringArray = {"apple", "banana", "cherry"};
        Stream<String> stringStream = Arrays.stream(stringArray);
        
        // 从数组的指定范围创建
        Stream<String> rangeStream = Arrays.stream(stringArray, 1, 3); // [banana, cherry]
        
        // 从基本类型数组创建
        int[] intArray = {1, 2, 3, 4, 5};
        IntStream intStream = Arrays.stream(intArray);
        
        // 从数组创建并处理
        double[] prices = {9.99, 19.99, 29.99, 39.99};
        double averagePrice = Arrays.stream(prices)
            .average()                                         // 计算平均值
            .orElse(0.0);                                     // 提供默认值
        System.out.println("平均价格: " + averagePrice);
        
        // 使用Stream.of()从可变参数创建
        Stream<String> varArgStream = Stream.of("red", "green", "blue");
        Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
    }
}

3. 生成Stream的各种方法

/**
 * 使用不同方法生成Stream
 */
public class StreamGeneration {
    
    public static void generateStreams() {
        // 1. 空Stream
        Stream<String> emptyStream = Stream.empty();
        
        // 2. 单个元素的Stream
        Stream<String> singleElementStream = Stream.of("单个元素");
        
        // 3. 使用Stream.builder()构建
        Stream<String> builtStream = Stream.<String>builder()
            .add("第一个元素")
            .add("第二个元素")
            .add("第三个元素")
            .build();
        
        // 4. 生成无限流 - Stream.generate()
        Stream<Double> randomNumbers = Stream.generate(Math::random)
            .limit(5);                                         // 限制为5个随机数
        
        Stream<String> constantStream = Stream.generate(() -> "Hello")
            .limit(3);                                         // 生成3个"Hello"
        
        // 5. 迭代生成 - Stream.iterate()
        Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2)
            .limit(10);                                        // 生成前10个偶数: 0,2,4,6,8,10,12,14,16,18
        
        // 生成斐波那契数列
        Stream<Long> fibonacci = Stream.iterate(new long[]{0, 1}, 
                array -> new long[]{array[1], array[0] + array[1]})
            .mapToLong(array -> array[0])                      // 提取数列值
            .limit(20)                                         // 前20个斐波那契数
            .boxed();                                          // 转换为Long对象流
        
        // Java 9+ 带条件的iterate
        // Stream<Integer> limitedIterate = Stream.iterate(1, n -> n <= 100, n -> n + 1);
        
        // 打印生成的数据进行验证
        System.out.println("前10个偶数:");
        Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
        
        System.out.println("前10个斐波那契数:");
        Stream.iterate(new long[]{0, 1}, arr -> new long[]{arr[1], arr[0] + arr[1]})
            .mapToLong(arr -> arr[0])
            .limit(10)
            .forEach(System.out::println);
    }
}

4. 从文件和其他资源创建Stream

import java.nio.file.*;
import java.util.regex.Pattern;

/**
 * 从文件和其他外部资源创建Stream
 */
public class FileAndResourceStreams {
    
    public static void createFromFiles() {
        try {
            // 1. 从文件读取行创建Stream
            Stream<String> fileLines = Files.lines(Paths.get("data.txt"));
            // 注意:需要使用try-with-resources或手动关闭流
            
            // 2. 安全的文件读取方式
            try (Stream<String> lines = Files.lines(Paths.get("application.log"))) {
                List<String> errorLines = lines
                    .filter(line -> line.contains("ERROR"))    // 过滤错误日志
                    .limit(100)                               // 只取前100条
                    .collect(Collectors.toList());
                System.out.println("错误日志条数: " + errorLines.size());
            }
            
            // 3. 遍历目录创建文件流
            try (Stream<Path> paths = Files.walk(Paths.get("src"))) {
                List<String> javaFiles = paths
                    .filter(Files::isRegularFile)            // 只要文件,不要目录
                    .filter(path -> path.toString().endsWith(".java")) // 只要Java文件
                    .map(Path::toString)                      // 转换为字符串路径
                    .collect(Collectors.toList());
                System.out.println("Java文件数量: " + javaFiles.size());
            }
            
            // 4. 查找文件
            try (Stream<Path> foundPaths = Files.find(Paths.get("src"), 
                    Integer.MAX_VALUE,                       // 搜索深度
                    (path, basicFileAttributes) -> 
                        path.getFileName().toString().contains("Test"))) {
                
                foundPaths.forEach(System.out::println);
            }
            
        } catch (Exception e) {
            System.err.println("文件操作错误: " + e.getMessage());
        }
    }
    
    public static void createFromOtherSources() {
        // 5. 从字符串创建字符流
        IntStream charStream = "Hello World".chars();
        String upperCaseLetters = charStream
            .filter(Character::isUpperCase)                   // 过滤大写字母
            .mapToObj(c -> (char) c)                         // 转换为字符
            .map(String::valueOf)                            // 转换为字符串
            .collect(Collectors.joining());                  // 连接字符串
        
        // 6. 从正则表达式分割创建流
        Pattern pattern = Pattern.compile(",");
        Stream<String> splitStream = pattern.splitAsStream("apple,banana,cherry,date");
        List<String> fruits = splitStream.collect(Collectors.toList());
        
        // 7. 从范围创建数值流
        IntStream range1 = IntStream.range(1, 10);           // [1,2,3,4,5,6,7,8,9]
        IntStream range2 = IntStream.rangeClosed(1, 10);     // [1,2,3,4,5,6,7,8,9,10]
        LongStream longRange = LongStream.rangeClosed(1L, 1000000L);
        
        // 8. 从Optional创建流 (Java 9+)
        Optional<String> optional = Optional.of("Hello");
        // Stream<String> optionalStream = optional.stream(); // Java 9+
        
        // Java 8中从Optional创建流的替代方法
        Stream<String> optionalStream = optional
            .map(Stream::of)                                 // 如果有值则创建单元素流
            .orElse(Stream.empty());                         // 否则创建空流
    }
}

中间操作详解

1. filter() - 过滤操作

/**
 * filter()操作的各种使用场景
 */
public class FilterOperations {
    
    public static void demonstrateFilter() {
        List<Person> people = Arrays.asList(
            new Person("张三", 25, "工程师", 8000),
            new Person("李四", 30, "设计师", 7000),
            new Person("王五", 35, "经理", 12000),
            new Person("赵六", 28, "工程师", 9000),
            new Person("孙七", 22, "实习生", 3000)
        );
        
        // 1. 基本过滤 - 过滤年龄大于25的人
        List<Person> adults = people.stream()
            .filter(person -> person.getAge() > 25)          // 年龄条件
            .collect(Collectors.toList());
        
        // 2. 多条件过滤 - 年龄大于25且薪水大于8000
        List<Person> seniorHighPaid = people.stream()
            .filter(person -> person.getAge() > 25)          // 第一个条件
            .filter(person -> person.getSalary() > 8000)     // 第二个条件
            .collect(Collectors.toList());
        
        // 或者合并为一个条件
        List<Person> seniorHighPaid2 = people.stream()
            .filter(person -> person.getAge() > 25 && person.getSalary() > 8000)
            .collect(Collectors.toList());
        
        // 3. 字符串过滤
        List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
        
        // 过滤长度大于5的单词
        List<String> longWords = words.stream()
            .filter(word -> word.length() > 5)
            .collect(Collectors.toList());
        
        // 过滤以特定字母开头的单词
        List<String> wordsStartingWithA = words.stream()
            .filter(word -> word.startsWith("a"))
            .collect(Collectors.toList());
        
        // 4. 空值过滤
        List<String> wordsWithNulls = Arrays.asList("apple", null, "banana", "", "cherry", null);
        List<String> nonNullNonEmpty = wordsWithNulls.stream()
            .filter(Objects::nonNull)                        // 过滤非null
            .filter(word -> !word.isEmpty())                 // 过滤非空字符串
            .collect(Collectors.toList());
        
        // 5. 使用方法引用进行过滤
        List<String> validEmails = Arrays.asList(
            "[email protected]", "invalid-email", "[email protected]", "bad@"
        );
        List<String> emails = validEmails.stream()
            .filter(FilterOperations::isValidEmail)          // 使用自定义方法
            .collect(Collectors.toList());
        
        // 6. 复杂对象属性过滤
        List<Person> engineers = people.stream()
            .filter(person -> "工程师".equals(person.getJob())) // 职业过滤
            .collect(Collectors.toList());
        
        // 7. 基于集合的过滤
        Set<String> allowedJobs = Set.of("工程师", "设计师");
        List<Person> allowedPeople = people.stream()
            .filter(person -> allowedJobs.contains(person.getJob()))
            .collect(Collectors.toList());
        
        System.out.println("过滤后的工程师: " + engineers.size() + "人");
    }
    
    // 自定义验证方法
    private static boolean isValidEmail(String email) {
        return email != null && 
               email.contains("@") && 
               email.contains(".") &&
               email.indexOf("@") < email.lastIndexOf(".");
    }
}

2. map() - 映射转换操作

/**
 * map()操作的各种转换场景
 */
public class MapOperations {
    
    public static void demonstrateMap() {
        List<Person> people = Arrays.asList(
            new Person("张三", 25, "工程师", 8000),
            new Person("李四", 30, "设计师", 7000),
            new Person("王五", 35, "经理", 12000)
        );
        
        // 1. 基本类型转换 - 提取姓名
        List<String> names = people.stream()
            .map(Person::getName)                            // 方法引用提取姓名
            .collect(Collectors.toList());
        
        // 2. 数值计算转换 - 计算年薪
        List<Integer> annualSalaries = people.stream()
            .map(person -> person.getSalary() * 12)          // 月薪转年薪
            .collect(Collectors.toList());
        
        // 3. 字符串转换操作
        List<String> words = Arrays.asList("apple", "banana", "cherry");
        
        // 转换为大写
        List<String> upperCaseWords = words.stream()
            .map(String::toUpperCase)                        // 方法引用转大写
            .collect(Collectors.toList());
        
        // 获取字符串长度
        List<Integer> wordLengths = words.stream()
            .map(String::length)                             // 获取长度
            .collect(Collectors.toList());
        
        // 字符串格式化
        List<String> formattedWords = words.stream()
            .map(word -> String.format("[%s]", word.toUpperCase()))
            .collect(Collectors.toList());
        
        // 4. 复杂对象转换 - 创建DTO对象
        List<PersonDTO> personDTOs = people.stream()
            .map(person -> new PersonDTO(
                person.getName().toUpperCase(),              // 姓名大写
                person.getAge(),
                person.getJob(),
                person.getSalary() * 12                      // 年薪
            ))
            .collect(Collectors.toList());
        
        // 5. 链式转换
        List<String> processedNames = people.stream()
            .map(Person::getName)                            // 提取姓名
            .map(String::toUpperCase)                        // 转大写
            .map(name -> "Mr/Ms. " + name)                   // 添加前缀
            .collect(Collectors.toList());
        
        // 6. 条件转换
        List<String> salaryLevels = people.stream()
            .map(person -> {
                if (person.getSalary() > 10000) {
                    return person.getName() + " - 高薪";
                } else if (person.getSalary() > 7000) {
                    return person.getName() + " - 中薪";
                } else {
                    return person.getName() + " - 低薪";
                }
            })
            .collect(Collectors.toList());
        
        // 7. 数学运算转换
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        // 平方
        List<Integer> squares = numbers.stream()
            .map(n -> n * n)
            .collect(Collectors.toList());
        
        // 数学函数应用
        List<Double> logarithms = numbers.stream()
            .map(Integer::doubleValue)                       // 转换为double
            .map(Math::log)                                  // 计算对数
            .collect(Collectors.toList());
        
        // 8. 复杂映射 - 从对象中提取多个属性组合
        List<String> personInfo = people.stream()
            .map(person -> String.format("%s (%d岁, %s, ¥%d)", 
                person.getName(), 
                person.getAge(), 
                person.getJob(),
                person.getSalary()))
            .collect(Collectors.toList());
        
        System.out.println("人员信息: " + personInfo);
    }
}

// DTO类用于演示对象转换
class PersonDTO {
    private String name;
    private int age;
    private String job;
    private int annualSalary;
    
    public PersonDTO(String name, int age, String job, int annualSalary) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.annualSalary = annualSalary;
    }
    
    // getters and toString...
}

3. flatMap() - 扁平化映射操作

/**
 * flatMap()操作用于处理嵌套结构和扁平化数据
 */
public class FlatMapOperations {
    
    public static void demonstrateFlatMap() {
        // 1. 基本扁平化 - 二维列表转一维
        List<List<String>> listOfLists = Arrays.asList(
            Arrays.asList("苹果", "香蕉"),
            Arrays.asList("樱桃", "枣子"),
            Arrays.asList("接骨木果", "无花果")
        );
        
        // 扁平化为单一列表
        List<String> flattenedFruits = listOfLists.stream()
            .flatMap(Collection::stream)                     // 将每个内部列表展开
            .collect(Collectors.toList());
        System.out.println("扁平化水果: " + flattenedFruits);
        
        // 2. 字符串分割扁平化
        List<String> sentences = Arrays.asList(
            "Java 是一种编程语言",
            "Stream 是 Java8 新特性", 
            "函数式编程很强大"
        );
        
        // 将句子分割成单词
        List<String> words = sentences.stream()
            .flatMap(sentence -> Arrays.stream(sentence.split(" "))) // 分割并扁平化
            .collect(Coll
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值