什么是Stream流

一、创建流

1.1 流的类型

在 Java 8 中, 集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流。
  • parallelStream() − 为集合创建并行流。

stream() 方法在 Collection接口

1.2 创建 Stream 的常见操作

举个例子,以 stream() 函数为例,可以通过集合、数组、I/O 通道等方式创建流。

//  从 List 创建流
List<String> list = Arrays.asList("apple", "banana", "orange");
Stream<String> streamFromList = list.stream();

// 从数组创建 IntStream:
int[] array = {1, 2, 3, 4, 5};
IntStream streamFromArray = Arrays.stream(array);

// 使用 Stream.of 创建流:
Stream<String> streamFromValues = Stream.of("apple", "banana", "orange");

事实上,创建流还有很多方式,更多细节查看 Creating Streams - Dev.java


二、 中间操作

中间操作是 map-filter-reduce 算法中 map 和 filter 实现。

中间操作会返回一个新的 Stream,因此可以链式调用。这些操作是惰性的,即它们不会立即执行,而是在有终端操作时才会执行。这意味着中间操作只是定义了流的转换规则,实际处理会推迟到终端操作执行时。

具体细节查看 Reducing a Stream - Dev.java

2.1 流的映射

(1) map()

映射流包括使用Function转换其元素。

您可以使用 map() 方法将一个流映射到另一个流,该方法将Function作为参数。映射流意味着该流处理的所有元素都将使用Function进行转换。举个例子,示例代码如下:

List<String> strings = List.of("one", "two", "three", "four");
Function<String, Integer> toLength = String::length;
Stream<Integer> ints = strings.stream()
                              .map(toLength);

需要注意的是,这段代码里面没有终端操作,因此这段代码没有做任何事

另外,有三种方法可以从 Stream 转到原始类型的流:mapToInt(), mapToLong() and mapToDouble()。举个例子,示例代码如下:

List<String> strings = List.of("one", "two", "three", "four");
IntSummaryStatistics stats = strings.stream()
                                    .mapToInt(String::length)
                                    .summaryStatistics();
System.out.println("stats = " + stats);

输出结果为

 stats = IntSummaryStatistics{count=4, sum=15, min=3, average=3,750000, max=5}

(2)flatmap()

在 Java 中,flatMap() 是流(Stream)类的一个中间操作,用于处理包含多个元素的流。它的作用是将每个流元素映射为一个流,然后将这些流合并成一个新的流

举个例子,假设有一个列表,其中包含多个单词,我们希望将每个单词拆分为字符,并将所有字符合并到一个新的流中:

List<String> words = Arrays.asList("Hello", "World");

// 使用 flatMap 将每个单词拆分为字符,并合并为一个新流
List<String> characters = words.stream()
                              .flatMap(word -> Arrays.stream(word.split("")))
                              .collect(Collectors.toList());

System.out.println(characters);

在这个例子中,flatMap 将每个单词映射为一个字符流,然后将这些字符流合并成一个新的流,最终得到一个包含所有字符的列表。输出结果为:

[H, e, l, l, o, W, o, r, l, d]

2.2 流的过滤

过滤是在流(Stream)上进行元素过滤。此方法适用于对象流和原始类型流。

举个例子,

List<String> strings = List.of("one", "two", "three", "four");
long count = strings.stream()
                    .map(String::length)
                    .filter(length -> length == 3)
                    .count();
System.out.println("count = " + count);   // 结果为:count = 2

其中,count()是 Stream API的另一个终端操作,它只计算已处理元素的数量

2.3 其他中间操作

  • distinct()方法 : 使用hashCode()和equals()方法来发现重复项, 然后去除重复项

  • sorted(): 有一个comparator的重载,该比较器将用于比较和排序流的元素。如果您不提供比较器,则Stream API假定您的流的元素具有可比性。如果不是,则会引发ClassCastException。

  • skip(long n) 方法: 用于跳过流中的前 n 个元素,返回一个新的流。如果流的长度小于 n,则返回一个空流。

  • limit(long maxSize) 方法: 用于限制流的大小,返回一个包含不超过 maxSize 个元素的新流。

  • concat() : 用于连接两个流,将它们合并成一个新的流。 建议用 flatMap() 来替代这个函数,举个例子:

    List<Integer> list0 = List.of(1, 2, 3);
    List<Integer> list1 = List.of(4, 5, 6);
    List<Integer> list2 = List.of(7, 8, 9);
    
    // 1st pattern: concat
    List<Integer> concat = 
        Stream.concat(list0.stream(), list1.stream())
              .collect(Collectors.toList());
    
    // 2nd pattern: flatMap
    List<Integer> flatMap =
        Stream.of(list0.stream(), list1.stream(), list2.stream())
              .flatMap(Function.identity())
              .collect(Collectors.toList());
    
    System.out.println("concat  = " + concat);
    System.out.println("flatMap = " + flatMap);
    
  • peek(): ,用于对流中的每个元素执行操作,同时保持流的原始结构。它返回的流与原始流相同,但可以在执行 peek() 时观察到中间的操作。这个可以用来调试流

    List<String> strings = List.of("one", "two", "three", "four");
    List<String> result =
            strings.stream()
                    .peek(s -> System.out.println("Starting with = " + s))
                    .filter(s -> s.startsWith("t"))
                    .peek(s -> System.out.println("Filtered = " + s))
                    .map(String::toUpperCase)
                    .peek(s -> System.out.println("Mapped = " + s))
                    .collect(Collectors.toList());
    System.out.println("result = " + result);
    

三、 终端操作

终端操作(Terminal Operations)是 map-filter-reduce 算法中 reduce 实现。

终端操作(Terminal Operations)是流操作中的最后一步,它们产生最终的结果或副作用。终端操作会触发流的遍历,并生成最终的结果或执行一些操作。在执行后,流就会被消耗,不能再使用。

如果流不以终端操作结束,则流不会处理任何数据。

3.1 reduce() 的使用方法

reduce() 方法: 对流中的元素进行归约操作,返回一个 Optional。(官方不建议使用这个方法)例如:

Optional<String> concatenated = words.stream().reduce((s1, s2) -> s1 + ", " + s2);

具体细节查看:Reducing a Stream - Dev.java

3.2 short-circuiting 方法

“short-circuiting” 方法是指在处理流时,当满足某个条件后,不再继续处理剩余的元素,提前结束处理流的过程。这可以帮助提高性能,因为它避免了对整个流的遍历。

在 Java 的 Stream API 中,有一些操作是 short-circuiting 的,例如:

  1. findFirst(): 返回第一个符合条件的元素后,立即结束流的处理。
  2. findAny(): 返回任意符合条件的元素后,立即结束流的处理。
  3. limit(n): 限制流的大小为 n,一旦达到这个大小,就不再继续处理。
  4. anyMatch(predicate): 一旦有任意一个元素满足条件,就结束流的处理。
  5. allMatch(predicate): 一旦有任意一个元素不满足条件,就结束流的处理。
  6. noneMatch(predicate): 一旦有任意一个元素满足条件,就结束流的处理。

3.3 其他的终端操作

需要注意的是,尽量避免使用 reduce() 方法

以下是一些常见的终端操作:

  1. forEach(): 对流中的每个元素执行指定的操作。例如:

    codeList<String> words = Arrays.asList("apple", "banana", "orange");
    words.stream().forEach(System.out::println);
    
  2. count(): 返回流中元素的数量。例如:

    long count = words.stream().count();
    
  3. collect(): 将流中的元素收集到一个集合或其他数据结构中。例如:

    List<String> collectedList = words.stream().collect(Collectors.toList());
    
  4. min() 和 max(): 返回流中的最小或最大元素。例如:

    Optional<String> minElement = words.stream().min(String::compareTo);
    Optional<String> maxElement = words.stream().max(String::compareTo);
    
  5. anyMatch()、allMatch() 和 noneMatch(): 用于检查流中的元素是否匹配给定的条件。例如:

    codeboolean anyMatch = words.stream().anyMatch(s -> s.startsWith("a"));
    boolean allMatch = words.stream().allMatch(s -> s.length() > 3);
    boolean noneMatch = words.stream().noneMatch(s -> s.contains("z"));
    

具体细节查看 Adding a Terminal Operation on a Stream - Dev.java


参考资料

Stream - Dev.java

相关推荐

  1. 什么Stream

    2024-07-12 02:38:02       21 阅读
  2. 什么数组

    2024-07-12 02:38:02       38 阅读
  3. <span style='color:red;'>Stream</span><span style='color:red;'>流</span>

    Stream

    2024-07-12 02:38:02      45 阅读
  4. Stream

    2024-07-12 02:38:02       59 阅读
  5. <span style='color:red;'>Stream</span><span style='color:red;'>流</span>

    Stream

    2024-07-12 02:38:02      47 阅读
  6. <span style='color:red;'>Stream</span><span style='color:red;'>流</span>

    Stream

    2024-07-12 02:38:02      32 阅读
  7. Stream

    2024-07-12 02:38:02       41 阅读
  8. <span style='color:red;'>Stream</span><span style='color:red;'>流</span>

    Stream

    2024-07-12 02:38:02      35 阅读
  9. <span style='color:red;'>Stream</span><span style='color:red;'>流</span>

    Stream

    2024-07-12 02:38:02      41 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-12 02:38:02       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-12 02:38:02       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-12 02:38:02       57 阅读
  4. Python语言-面向对象

    2024-07-12 02:38:02       68 阅读

热门阅读

  1. playwright下载文件如何不被删除

    2024-07-12 02:38:02       17 阅读
  2. c#中的超时终止

    2024-07-12 02:38:02       18 阅读
  3. 归并排序算法Python实现

    2024-07-12 02:38:02       22 阅读
  4. 07-7.4.2 B+树

    2024-07-12 02:38:02       19 阅读
  5. 生信技能52 - VCF文件hg38与hg19坐标相互转换

    2024-07-12 02:38:02       20 阅读
  6. 技术总结(1)——方向与成长思考

    2024-07-12 02:38:02       23 阅读
  7. 《穿透财报:读懂财报中的逻辑与陷阱》

    2024-07-12 02:38:02       21 阅读
  8. Spring——自动装配Bean

    2024-07-12 02:38:02       21 阅读
  9. 前端高頻面試題(一)

    2024-07-12 02:38:02       22 阅读
  10. SpringBoot常见注解

    2024-07-12 02:38:02       19 阅读
  11. linux lvm使用

    2024-07-12 02:38:02       20 阅读
  12. ETag:Springboot接口如何添加Tag

    2024-07-12 02:38:02       22 阅读
  13. 3. 排序算法代码-python

    2024-07-12 02:38:02       23 阅读
  14. android 图片轮播

    2024-07-12 02:38:02       19 阅读
  15. ubuntu 检查硬盘的通电时长、健康度

    2024-07-12 02:38:02       24 阅读