Java stream的用法

41 阅读5分钟

流的创建:

  • List, Array, File, IO, ...

    // create stream from list
    List<Integer> list = List.of(1, 2, 3);
    list.stream().filter(i -> i > 2).forEach(System.out::println);
    // create stream from array
    Integer[] array = new Integer[]{1, 2, 3};
    Stream<Integer> arrStream = Arrays.stream(array);
    // create stream from Stream.of
    Stream<Integer> ofStream = Stream.of(1, 2, 3);
    // use stream builder
    Stream<Integer> builderStream = Stream.<Integer>builder().add(1).add(2).add(3).build();
    // create stream from files
    Path path = Paths.get("file.txt");
        try {
            Stream<String> fileStream = Files.lines(path);
            fileStream.forEach(System.out::println);
        } catch (IOException e) {
            System.err.println("读取文件失败: " + e.getMessage());
        }
    
    // create stream from range
    IntStream intStream = IntStream.range(1, 10);
    System.out.println(intStream.count());
    // 无限流创建 infinite qequential stream
    Stream.generate(() -> "hello"); // Returns an infinite sequential unordered stream where each element is generated by the provided Supplie
    Stream<Double> limit = Stream.generate(Math::random).limit(10);

    Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(10);
    iterateStream.forEach(System.out::println);

    // parallel stream
    Stream<Integer> parallelStream = list.parallelStream();
    parallelStream.forEach(System.out::println);

中间操作 Intermediate Operations

如 Filter Map Sort ... 每一次中间操作都产生一个Stream流,从而支持链式调用 中间操作是惰性执行的,意味着它们不会立即执行,遇到终端操作才会执行

终端操作 Terminal Operations如 Collect Reduce Count Match ... ForEach 流中的元素会被消费,流就不能在被操作了

不熟悉不常用的流强调一下:

Stream API提供了IntStream, LongStream和DoubleStream来分别处理Int long double类型数据

IntStream intStream = IntStream.range(1, 4);
// 生成1, 2, 3 --左闭右开

Stream<Integer> stream = intStream.boxed();// 转成普通Stream对象

无限流:生成无限元素的流,一般会用limit限制

Stream<Double> limit = Stream.generate(Math::random).limit(10);

Iterate方法:生成数学序列或者是先迭代算法

Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(10);// 生成等差数列
Stream<T> iterate = Stream.iterate(0, n -> n <= 10, n -> n + 2);// 起始元素、筛选条件、生成下一个元素的函数。since java9

中间操作 Intermediate operations对流中的元素进行处理,如筛选、映射、排序等

根据操作的性质分为以下几个类别

  1. Filtering and Slicing用于过滤或缩减流中的元素数量:filter/distinct/limit/skip...

    List<String> list = List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j");
    list.stream()
            .filter(s -> s.startsWith("a"))
            .map(String::toUpperCase)
            .forEach(System.out::println);
    
    list.stream()
            .limit(3)// limit the stream to the first 3 elements
            .forEach(System.out::println);
    
    list.stream()
            .skip(3)// skip the first 3 elements
            .forEach(System.out::println);
  1. Mapping映射,用于转换流中的元素或提取元素的特定属性:map/flatMap/mapToInt...
    List<String> list = List.of("a", "b", "c", "d", "e");
    list.stream()
            .map(String::toUpperCase)
            .forEach(System.out::println);

Map方法适用于单层结构的流,进行元素一对一的转换,例如更改数据类型或提取信息。但对于嵌套的集合、数组或其他多层结构的数据处理不够灵活,在这些情况下flatMap成为更合适的选择。它不仅能够执行map的转换功能,还能够扁平化多层数据结构,将它们转换合并成一个单层流。

过程是这样的:首先它先创建一个流,以下一层结构的数据作为流的元素,然后再将该数据结构元素转换为独立的流,最后将它们合并成一个单层流

Screenshot 2025-10-04 211145.jpg


List<List<Person>> personGroup = List.of(
        List.of(new Person("a", 10), new Person("b", 20)),
        List.of(new Person("c", 30), new Person("d", 40)),
        List.of(new Person("e", 50), new Person("f", 60))
);
personGroup.stream()
        .flatMap(List::stream)
        .forEach(System.out::println);
  1. Sorting排序

    Stream.of("apple", "blueberry", "cherry", "orange", "pear")
            .sorted()
            .forEach(System.out::println);
    Stream.of("apple", "blueberry", "cherry", "orange", "pear")
            .sorted(Comparator.comparingInt(String::length))
            .forEach(System.out::println);

结束操作 Terminal Operations

分为:

Search and match short

  • circuiting operations短路操作
  1. anyMatch
  2. nonMatch
  3. allMatch
  4. findFirst
  5. findAny
    List<Person> list = List.of(new Person("a", 10),
            new Person("b", 20),
            new Person("c", 30),
            new Person("d", 40),
            new Person("e", 50));
    boolean anyMatch = list.stream().anyMatch(p -> p.age > 30);
    boolean noneMatch = list.stream().noneMatch(p -> p.age > 50);
    boolean allMatch = list.stream().allMatch(p -> p.age > 10);
    System.out.println("anyMatch: " + anyMatch);
    System.out.println("noneMatch: " + noneMatch);
    System.out.println("allMatch: " + allMatch);
    list.stream()
            .filter(p -> p.age > 30)
            .findFirst() // return a Optional<T> because it may not exist
            .ifPresent(System.out::println);
    list.stream()
            .filter(p -> p.age > 30)
            .findAny()
            .ifPresent(System.out::println);

Aggregation

  • 聚合操作是规约操作Reduction operation的一种特殊形式
  1. count - 可以直接用Stream,也可以转成IntStream(无必要),返回值是Optional
  2. max - 可以直接用Stream,也可以转成IntStream,返回值是Optional
  3. min - 可以直接用Stream,也可以转成IntStream,返回值是Optional
  4. average - 需要转成基本数据流IntStream,求平均值后返回值是OptionalDouble
  5. sum - 需要转成基本数据流IntStream,求sum后结果是int

        List<Integer> integers = List.of(11, 22, 33, 44, 55);
        // can use Stream or IntStream
        System.out.println("count: " + integers.stream().count());
        System.out.println("count: " + integers.stream().mapToInt(i -> i).count());// use mapToInt instead of map
        // map or mapToInt
        integers.stream().max(Integer::compareTo).ifPresent(n -> System.out.println("max: " + n));
        integers.stream().mapToInt(i -> i).max().ifPresent(n -> System.out.println("max: " + n));
        // min or mapToInt
        integers.stream().min(Integer::compareTo).ifPresent(n -> System.out.println("min: " + n));
        integers.stream().mapToInt(i -> i).min().ifPresent(n -> System.out.println("min: " + n));
        // must use mapToInt to get average or sum
        integers.stream().mapToInt(i -> i).average().ifPresent(i -> System.out.println("average: " + i));
        System.out.println("sum: " + integers.stream().mapToInt(i -> i).sum());

Reduction

  • 更为通用,通过一个自定义的累加器函数,对流中的所有元素进行迭代处理,以累计出迭代结果,你可以实现任何类型的结果汇总,不仅是数学上的聚合,而是任何形式的数据合并,比如拼接字符串, 合并集合等。
  1. reduce
  • reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型。比如,之前提到count、min和max方法,因为常用而被纳入标准库中。事实上,这些方法都是reduce操作。

    List<Integer> list = List.of(1, 2, 3, 4, 5);
    list.stream().reduce(0, (a, b) -> a + b);// identity accumulator
    list.stream().reduce(Integer::sum).ifPresent(System.out::println);// accumulator
    System.out.println(list.stream().reduce(0, Integer::sum));

Collection

  1. collect

        List<Person> list = List.of(new Person("a", 10),
                new Person("b", 20),
                new Person("c", 30),
                new Person("d", 40),
                new Person("e", 50));
        list.stream()
                .filter(p -> p.age > 30)
//                .toList()
                .collect(Collectors.toList())
                .forEach(System.out::println);

        list.stream()
                .filter(p -> p.age > 30)
                .collect(Collectors.toMap(Person::getName, Person::getAge))
                .forEach((k, v) -> System.out.println(k + ": " + v));

        list.stream()
                .collect(Collectors.groupingBy(Person::getName))
                .forEach((k, v) -> System.out.println(k + ": " + v));

        list.stream()
                .collect(Collectors.partitioningBy(p -> p.getAge() > 18))
                .forEach((k, v) -> System.out.println(k + ": " + v));

        System.out.println(list.stream()
                .map(Person::getName)
                .collect(Collectors.joining(",")));

        IntSummaryStatistics statistics = list.stream()
                .collect(Collectors.summarizingInt(Person::getAge));
        System.out.println(statistics.getMax());
        System.out.println(statistics.getAverage());
        System.out.println(statistics.getSum());

Iteration

  1. forEach

Parallel Stream 并行流

  • 核心工作原理:并行流在开始时,spliterator分割迭代器将数据分割成多个片段,分割采用迭代的方式动态进行,然后fork/join框架将数据片段分配到多个线程和处理器核心上进行并行处理,处理完后将数据汇总合并。
    List.of("A", "B", "C", "D", "E", "F", "G", "H", "I", "J")
            .parallelStream()
            .map(String::toLowerCase)
            .forEach(System.out::println);// 因为并行流是多线程的,所以结果是乱序的
            ```

    List.of("H", "E", "L", "L", "O", "W", "O", "R", "L", "D")
            .parallelStream()
            .map(String::toLowerCase)
            .forEachOrdered(System.out::print);// 结果是按顺序的