10分钟带你学会Stream流操作

Stream流操作

什么,你还不会Stream操作?不急,我们一步一步来学习Stream流吧。

1.Stream介绍
  • Stream(流) 是 Java 8 引入的核心特性之一,它专注于对集合(或其他数据源)进行高效的聚合操作(如过滤、映射、排序、统计等),支持函数式编程风格,让代码更简洁、可读性更强。

    简单说,Stream 的作用是:用声明式的方式(“做什么” 而非 “怎么做”)处理集合数据,替代传统的 for 循环遍历,简化代码。

2.Stream特点
  1. 不改变数据源:所有操作都不会修改原始集合,而是返回新的流或结果。

  2. 惰性执行:中间操作(如过滤、映射)不会立即执行,只有当触发 “终止操作” 时才会一次性执行,提高效率。

  3. 一次性使用:一个流只能被消费一次(执行终止操作后,流就关闭了,不能再复用)。

  4. 支持并行处理:通过 parallelStream() 可轻松实现并行操作,利用多核 CPU 提高处理大数据的效率。

3.Stream流程操作

使用 Stream 通常分为 3 个步骤:

  1. 创建流:从数据源(集合、数组等)获取流;

  2. 中间操作:对数据进行处理(如过滤、转换、排序等),返回新的流;

  3. 终止操作:触发实际计算,返回处理结果(如列表、统计值等)。

4.创建Stream流
  • 从集合(Collection)创建Stream流

    这是最常用的方式,所有实现 Collection 接口的类(如 ListSet)都可以通过 stream()parallelStream() 方法创建流:

    • stream():创建串行流(单线程处理)

    • parallelStream():创建并行流(多线程处理,适合大数据量)

      import java.util.Arrays;
      import java.util.List;
      import java.util.Set;
      import java.util.HashSet;
      import java.util.stream.Stream;
      ​
      public class CreateStream {
          public static void main(String[] args) {
              // 从 List 创建流
              List<String> list = Arrays.asList("a", "b", "c");
              Stream<String> listStream = list.stream(); // 串行流
              Stream<String> parallelListStream = list.parallelStream(); // 并行流
              
              // 从 Set 创建流
              Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3));
              Stream<Integer> setStream = set.stream();
          }
      }
  • 从数组创建Stream流

    通过 Arrays.stream() 方法可将数组转换为流,支持基本类型数组(如 int[]double[])和对象数组:

    import java.util.Arrays;
    import java.util.stream.IntStream;
    import java.util.stream.Stream;
    ​
    public class CreateStream {
        public static void main(String[] args) {
            // 对象数组 → Stream
            String[] strArray = {"x", "y", "z"};
            Stream<String> strStream = Arrays.stream(strArray);
            
            // 基本类型数组 → 对应的流(如 IntStream、DoubleStream)
            int[] intArray = {10, 20, 30};
            IntStream intStream = Arrays.stream(intArray); // 专门处理int的流,更高效
        }
    }
  • 直接生成流(Stream.of

    使用 Stream.of() 可直接传入元素生成流,适合已知具体元素的场景:

    import java.util.stream.Stream;
    ​
    public class CreateStream {
        public static void main(String[] args) {
            // 传入多个元素
            Stream<String> stream1 = Stream.of("apple", "banana", "orange");
            
            // 传入数组(等价于 Arrays.stream())
            Integer[] numArray = {1, 2, 3};
            Stream<Integer> stream2 = Stream.of(numArray);
        }
    }
  • 创建空流(Stream.empty()

    当需要返回一个 “空流”(而非 null)时,使用 Stream.empty(),避免空指针异常:

    import java.util.stream.Stream;
    ​
    public class CreateStream {
        public static void main(String[] args) {
            Stream<String> emptyStream = Stream.empty();
            System.out.println(emptyStream.count()); // 输出:0(空流的元素数量为0)
        }
    }
  • 生成无限流(generate()iterate()

    通过 Stream.generate()Stream.iterate() 可创建无限流(元素数量无限),需配合 limit(n) 限制元素数量:

    • Stream.generate(Supplier):通过 Supplier 接口生成元素(每次调用生成一个新元素)

    • Stream.iterate(初始值, UnaryOperator):从初始值开始,通过迭代函数生成下一个元素

    import java.util.stream.Stream;
    ​
    public class CreateStream {
        public static void main(String[] args) {
            // 1. generate():生成5个随机数(DoubleStream)
            Stream<Double> randomStream = Stream.generate(Math::random)
                                               .limit(5); // 限制为5个元素
            randomStream.forEach(System.out::println); // 输出5个0-1之间的随机数
            
            // 2. iterate():生成10以内的奇数(1,3,5,7,9)
            Stream<Integer> oddStream = Stream.iterate(1, n -> n + 2)
                                             .limit(5); // 限制5个元素
            oddStream.forEach(System.out::println); // 输出:1 3 5 7 9
        }
    }
  • 从文件生成流(JDK8+)

    通过 Files.lines() 可将文件内容按行转换为流(每行作为一个元素):

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.stream.Stream;
    ​
    public class CreateStream {
        public static void main(String[] args) {
            // 从文件读取行,生成流(需处理异常)
            try (Stream<String> fileStream = Files.lines(Paths.get("test.txt"))) {
                fileStream.forEach(System.out::println); // 打印文件每行内容
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

5.Stream中间操作

Stream 流的中间操作(Intermediate Operations) 是对数据流进行处理和转换的操作,它们的特点是惰性执行(仅定义操作规则,不立即执行),且返回一个新的流,因此可以链式调用多个中间操作。

  • 过滤(filter()

    • 作用:保留满足条件的元素(通过 Predicate 函数判断)。

    • 示例:过滤出长度大于 3 的字符串

      import java.util.Arrays;
      import java.util.List;
      import java.util.stream.Collectors;
      ​
      public class StreamIntermediate {
          public static void main(String[] args) {
              List<String> words = Arrays.asList("a", "bb", "ccc", "dddd", "eeeee");
              
              // 过滤出长度 > 3 的元素
              List<String> result = words.stream()
                      .filter(word -> word.length() > 3) // 中间操作:过滤
                      .collect(Collectors.toList());     // 终止操作:收集结果
              
              System.out.println(result); // 输出:[dddd, eeeee]
          }
      }

  • 映射(map()

    • 作用:将元素转换为另一种类型(通过 Function 函数映射)。

    • 示例:将字符串转换为其长度

      List<String> words = Arrays.asList("apple", "banana", "cat");
      ​
      // 字符串 → 长度(Integer类型)
      List<Integer> lengths = words.stream()
              .map(String::length) // 等价于 word -> word.length()
              .collect(Collectors.toList());
      ​
      System.out.println(lengths); // 输出:[5, 6, 3]

  • 扁平映射(flatMap()

    • 作用:将 “流的流”(如 Stream<List<T>>)扁平化为单个流(Stream<T>),常用于处理嵌套集合。

    • 示例:将多个列表的元素合并为一个流

      List<List<Integer>> nestedList = Arrays.asList(
          Arrays.asList(1, 2),
          Arrays.asList(3, 4, 5),
          Arrays.asList(6)
      );
      
      // 扁平化为单个整数流
      List<Integer> flatList = nestedList.stream()
              .flatMap(List::stream) // 将每个子列表转为流,再合并
              .collect(Collectors.toList());
      
      System.out.println(flatList); // 输出:[1, 2, 3, 4, 5, 6]

  • 排序(sorted()

    • 作用:对元素进行排序,有两种形式:

      • sorted():按元素自然顺序排序(需实现 Comparable 接口)。

      • sorted(Comparator):按自定义比较器排序。

    • 示例:对整数和字符串排序

      // 自然排序(整数)
      List<Integer> nums = Arrays.asList(3, 1, 4, 2);
      List<Integer> sortedNums = nums.stream()
              .sorted()
              .collect(Collectors.toList());
      System.out.println(sortedNums); // 输出:[1, 2, 3, 4]
      
      // 自定义排序(字符串按长度)
      List<String> words = Arrays.asList("banana", "apple", "cat");
      List<String> sortedWords = words.stream()
              .sorted((s1, s2) -> Integer.compare(s1.length(), s2.length()))
              .collect(Collectors.toList());
      System.out.println(sortedWords); // 输出:[cat, apple, banana]

  • 去重(distinct()

    • 作用:去除流中重复的元素(基于 equals() 方法判断)。

    • 示例:去除重复整数

      List<Integer> nums = Arrays.asList(2, 1, 3, 2, 1);
      List<Integer> distinctNums = nums.stream()
              .distinct()
              .collect(Collectors.toList());
      
      System.out.println(distinctNums); // 输出:[2, 1, 3]

  • 限制(limit()

    • 作用:截取流的前 n 个元素。

    • 示例:取前 3 个元素

      List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
      List<Integer> limited = nums.stream()
              .limit(3)
              .collect(Collectors.toList());
      
      System.out.println(limited); // 输出:[1, 2, 3]

  • 跳过(skip()

    • 作用:跳过流的前 n 个元素。

    • 示例:跳过前 2 个元素

      List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
      List<Integer> skipped = nums.stream()
              .skip(2)
              .collect(Collectors.toList());
      
      System.out.println(skipped); // 输出:[3, 4, 5]

  • 调试(peek()

    • 作用:对元素执行操作(如打印、日志),但不改变元素本身,常用于调试。

    • 示例:打印流中元素的中间状态

      List<Integer> nums = Arrays.asList(1, 2, 3);
      List<Integer> result = nums.stream()
              .peek(num -> System.out.println("处理前:" + num)) // 调试打印
              .map(num -> num * 2)
              .peek(num -> System.out.println("处理后:" + num))
              .collect(Collectors.toList());
      
      // 输出:
      // 处理前:1
      // 处理后:2
      // 处理前:2
      // 处理后:4
      // 处理前:3
      // 处理后:6

6.Stream终止操作
  • 收集结果:将流转换为集合/其他数据结构

    这类操作通过 Collectors 工具类提供的方法,将流中的元素收集为 ListSetMap 等常见数据结构,是最常用的终止操作之一。

    • collect(Collectors.toList()):收集为List

      import java.util.Arrays;
      import java.util.List;
      import java.util.stream.Collectors;
      
      public class StreamTerminal {
          public static void main(String[] args) {
              List<String> fruits = Arrays.asList("apple", "banana", "cherry", "apple");
              
              // 收集所有元素为 List(允许重复)
              List<String> fruitList = fruits.stream()
                      .filter(f -> f.startsWith("a")) // 中间操作:过滤以"a"开头的水果
                      .collect(Collectors.toList()); // 终止操作:收集为 List
              
              System.out.println(fruitList); // 输出:[apple, apple]
          }
      }

    • collect(Collectors.toSet()):收集为set

      // 收集为 Set(自动去重)
      List<String> fruits = Arrays.asList("apple", "banana", "cherry", "apple");
      List<String> fruitSet = fruits.stream()
              .collect(Collectors.toSet()) // 终止操作:收集为 Set
              .stream().collect(Collectors.toList()); // 转为 List 方便打印
      
      System.out.println(fruitSet); // 输出:[apple, banana, cherry](去重)

    • collect(Collectors.joining()):拼接为字符串

      // 拼接流中的字符串(支持自定义分隔符、前缀、后缀)
      List<String> words = Arrays.asList("Hello", "Stream", "World");
      String joined = words.stream()
              .collect(Collectors.joining(", ", "【", "】")); // 分隔符:,  前缀:【  后缀:】
      
      System.out.println(joined); // 输出:【Hello, Stream, World】

  • 遍历元素

    forEach() 用于遍历流中的每个元素,执行自定义操作(如打印、修改元素属性),无返回值(void),是简单的 “消费型” 终止操作。

    List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
    
    // 遍历并打印每个元素(配合 Lambda 表达式)
    nums.stream()
            .filter(n -> n % 2 == 0) // 中间操作:筛选偶数
            .forEach(n -> System.out.print(n + " ")); // 终止操作:遍历打印
    
    // 输出:2 4 

  • 统计与聚合

    这类操作用于对数值型流(如 IntStreamLongStream)或普通流中的元素进行统计,返回单个数值(如计数、求和、最值、平均值)。

    • count():统计元素个数

      List<String> words = Arrays.asList("a", "bb", "ccc", "dddd");
      
      // 统计长度 > 2 的元素个数
      long count = words.stream()
              .filter(w -> w.length() > 2)
              .count(); // 终止操作:返回 long 类型的计数
      
      System.out.println(count); // 输出:2("ccc" 和 "dddd")

    • max(Comparator)/min(Comparator):获取最值

      List<Integer> scores = Arrays.asList(85, 92, 78, 95, 88);
      
      // 获取最高分(max)
      Integer maxScore = scores.stream()
              .max(Integer::compare) // 终止操作:按自然顺序取最大值(返回 Optional)
              .orElse(0); // 若流为空,返回默认值 0
      
      // 获取最低分(min)
      Integer minScore = scores.stream()
              .min(Integer::compare)
              .orElse(0);
      
      System.out.println("最高分:" + maxScore); // 输出:95
      System.out.println("最低分:" + minScore); // 输出:78

    • sum()/average():求和与平均数(仅数值流)

      List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
      
      // 求和(先转为 IntStream)
      int sum = nums.stream()
              .mapToInt(Integer::intValue) // 转为 IntStream
              .sum(); // 终止操作:求和
      
      // 求平均值(返回 OptionalDouble,避免空流无均值的问题)
      double avg = nums.stream()
              .mapToInt(Integer::intValue)
              .average()
              .orElse(0.0);
      
      System.out.println("总和:" + sum); // 输出:15
      System.out.println("平均值:" + avg); // 输出:3.0

  • 判断与匹配

    这类操作用于判断流中元素是否满足特定条件,返回 boolean 类型结果,支持 “存在匹配”“全部匹配”“无匹配” 三种场景。

    • anyMatch(Predicate):是否存在一个元素满足条件

      List<String> fruits = Arrays.asList("apple", "banana", "cherry");
      
      // 判断是否存在以"b"开头的水果
      boolean hasB = fruits.stream()
              .anyMatch(f -> f.startsWith("b")); // 终止操作:存在匹配则返回 true
      
      System.out.println(hasB); // 输出:true("banana" 满足)

    • allMatch(Predicate):是否存在一个元素满足条件

      // 判断所有水果的长度是否 > 3
      boolean allLong = fruits.stream()
              .allMatch(f -> f.length() > 3);
      
      System.out.println(allLong); // 输出:false("apple" 长度 5>3,但 "cherry" 长度 6>3?不,这里例子中 fruits 是 ["apple","banana","cherry"],三者长度都>3?修正:若改为 ["apple","b","cherry"],则输出 false)

    • noneMatch(Predicate):是否存在一个元素满足条件

      // 判断是否没有以"x"开头的水果
      boolean noX = fruits.stream()
              .noneMatch(f -> f.startsWith("x"));
      
      System.out.println(noX); // 输出:true(没有以"x"开头的水果)

  • 获取单个元素

    用于从流中获取单个元素,返回 Optional<T> 类型(避免空指针异常,需处理 “无元素” 的情况)。

    • findFirst():获取流中的第一个元

      List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
      
      // 获取第一个偶数
      Integer firstEven = nums.stream()
              .filter(n -> n % 2 == 0)
              .findFirst() // 终止操作:返回第一个元素的 Optional
              .orElse(-1);
      
      System.out.println(firstEven); // 输出:2

    • findAny():获取流中的任意一个元素(并行流中更高效)

      // 并行流中获取任意一个偶数(串行流中通常也是第一个,但并行流可能返回任意一个)
      Integer anyEven = nums.parallelStream()
              .filter(n -> n % 2 == 0)
              .findAny()
              .orElse(-1);
      
      System.out.println(anyEven); // 输出:2 或 4(并行流中结果可能不同)

学会这些就差不多可以了(可以应对大部分场景了),嘿嘿嘿
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值