JavaRush /Java 博客 /Random-ZH /喝咖啡休息#177。Java 8 中的 Java Stream 详细指南

喝咖啡休息#177。Java 8 中的 Java Stream 详细指南

已在 Random-ZH 群组中发布
来源:Hackernoon 这篇文章提供了有关使用 Java Stream 的详细教程以及代码示例和解释。 喝咖啡休息#177。 Java 8 中的 Java Stream 详细指南 - 1

Java 8 中的 Java 线程简介

Java Streams 作为 Java 8 的一部分引入,用于处理数据集合。它们本身不是数据结构,但可用于通过排序和流水线操作从其他数据结构输入信息以产生最终结果。 注意:不要混淆 Stream 和 Thread,这一点很重要,因为在俄语中,这两个术语通常在同一个翻译中被称为“流”。Stream 表示用于执行操作(最常见的是传输或存储数据)的对象,而 Thread(字面翻译 - 线程)表示允许您与其他代码分支并行执行某些程序代码的对象。 因为 Stream 不是一个单独的数据结构,所以它永远不会改变数据源。Java流具有以下特点:
  1. Java Stream 可以通过“java.util.stream”包来使用。可以使用以下代码将其导入到脚本中:

    import java.util.stream.* ;

    使用这段代码,我们还可以轻松实现Java Stream中的几个内置函数。

  2. Java Stream可以接受来自数据集合的输入,例如Java中的集合和数组。

  3. Java Stream 不需要更改输入数据结构。

  4. Java Stream 不会更改源。相反,它使用适当的管道方法生成输出。

  5. Java Streams 受中间和终端操作的影响,我们将在以下部分中讨论。

  6. 在 Java Stream 中,中间操作是管道化的,并以惰性求值格式发生。它们以终端函数结束。这构成了使用 Java Stream 的基本格式。

在下一节中,我们将了解 Java 8 中用于在需要时创建 Java Stream 的各种方法。

在 Java 8 中创建 Java 流

Java 线程可以通过多种方式创建:

1.使用Stream.empty()方法创建一个空流

您可以创建一个空流以供稍后在代码中使用。如果使用Stream.empty()方法,将生成一个空流,不包含任何值。如果我们想在运行时跳过空指针异常,这个空流会派上用场。为此,您可以使用以下命令:
Stream<String> str = Stream.empty();
上面的语句将生成一个名为str的空流,其中不包含任何元素。要验证这一点,只需使用术语str.count()检查流的数量或大小。例如,
System.out.println(str.count());
结果,我们在输出处得到0

2. 使用 Stream.builder() 方法和 Stream.Builder 实例创建流

我们还可以使用Stream Builder使用构建器设计模式创建流。它是为逐步构建对象而设计的。让我们看看如何使用Stream Builder实例化流。
Stream.Builder<Integer> numBuilder = Stream.builder();

numBuilder.add(1).add(2).add( 3);

Stream<Integer> numStream = numBuilder.build();
使用此代码,您可以创建一个名为numStream的包含int elements 的流。由于首先创建了 名为numBuilder的Stream.Builder实例,一切都很快完成。

3.使用Stream.of()方法创建具有指定值的流

创建流的另一种方法涉及使用Stream.of()方法。这是创建具有给定值的流的简单方法。它声明并初始化线程。使用Stream.of()方法创建流的示例:
Stream<Integer> numStream = Stream.of(1, 2, 3);
此代码将创建一个包含int元素 的流,就像我们在前面使用Stream.Builder 的方法中所做的那样。这里我们直接使用Stream.of()创建了一个具有预定义值[1, 2 和 3]的流。

4. 使用 Arrays.stream() 方法从现有数组创建流

创建线程的另一种常见方法是在 Java 中使用数组。这里的流是使用Arrays.stream()方法从现有数组创建的。所有数组元素都转换为流元素。这是一个很好的例子:
Integer[] arr = {1, 2, 3, 4, 5};

Stream<Integer> numStream = Arrays.stream(arr);
此代码将生成一个numStream,其中包含名为 arr 的数组的内容,该数组是一个整数数组。

5. 使用 Stream.concat() 方法合并两个现有流

另一种可用于创建流的方法是Stream.concat()方法。它用于组合两个线程以创建单个线程。两个流按顺序组合。这意味着第一个线程先出现,然后是第二个线程,依此类推。此类串联的示例如下所示:
Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);

Stream<Integer> numStream2 = Stream.of(1, 2, 3);

Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
上面的语句将创建一个名为combinedStream的最终流,其中逐一包含第一个流numStream1和第二个流numStream2的元素。

Java Stream 的操作类型

如前所述,您可以在 Java 8 中使用 Java Stream 执行两种类型的操作:中间操作和终端操作。让我们更详细地看看它们中的每一个。

中间操作

中间操作生成输出流,并且仅在遇到终端操作时才执行。这意味着中间操作是延迟执行的、管道化的,并且只能由终端操作完成。稍后您将了解惰性求值和管道。中间操作的示例有以下方法:filter()map()difference()peek()sorted()等。

终端操作

终端操作完成中间操作的执行,同时返回输出流的最终结果。由于终端操作标志着延迟执行和流水线操作的结束,因此该线程在经历终端操作后不能再次使用。终端操作的示例有以下方法:forEach()collect()count()reduce()等。

Java Stream 的操作示例

中间操作

以下是一些可应用于 Java Stream 的中间操作的示例:

筛选()

此方法用于过滤流中与 Java 中的特定谓词匹配的元素。然后,这些过滤的项目组成一个新的流。让我们看一个例子来更好地理解。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
结论:
[98]
说明:在此示例中,您可以看到,即使是元素(可被 2 整除)也会使用filter()方法 进行过滤,并存储在整数列表numStream中,稍后会打印该列表的内容。由于 98 是流中唯一的偶数,因此它会打印在输出中。

地图()

此方法用于通过对原始输入流的元素执行映射函数来创建新流。也许新流具有不同的数据类型。该示例如下所示:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
结论:
[86、130、2、196、126]
说明:在这里我们看到map()方法用于简单地将numStream流的每个元素加倍。从输出中可以看到,流中的每个元素都已成功加倍。

清楚的()

此方法用于通过过滤掉重复项来仅检索流中的单个元素。相同的示例如下所示:
Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
结论:
[43、65、1、98、63]
说明:在这种情况下,different()方法用于numStream它通过从流中删除重复项来检索 numList 中的所有单个元素。正如您从输出中看到的,没有重复项,这与输入流不同,输入流最初有两个重复项(63 和 1)。

窥视()

此方法用于在执行终端操作之前跟踪中间更改。这意味着peek()可用于对流的每个元素执行操作,以创建可以执行进一步中间操作的流。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
结论:
映射:430 映射:650 映射:10 映射:980 映射:630 [430、650、10、980、630]
说明:这里peek()方法用于生成中间结果,因为map()方法应用于流的元素。这里我们可以注意到,即使在使用collect()终端操作在打印语句中打印列表的最终内容之前,每个流元素映射的结果也会提前按顺序打印。

排序()

Sorted()方法用于对流的元素进行排序。默认情况下,它按升序对元素进行排序。您还可以指定特定的排序顺序作为参数。此方法的示例实现如下所示:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
结论:
1 43​​ 63 65 98
说明:这里,sorted()方法用于默认按升序对流中的元素进行排序(因为没有指定具体顺序)。您可以看到输出中打印的项目按升序排列。

终端操作

以下是一些可应用于 Java 流的终端操作的示例:

forEach()

forEach()方法用于迭代流的所有元素,并对每个元素一一执行该函数。这可以作为循环语句(例如forwhile等)的替代语句。例子:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
结论:
43 65 1 98 63
说明:这里的forEach()方法用于逐个打印流中的每个元素。

数数()

count()方法用于检索流中存在的元素总数。它类似于size()方法,通常用于确定集合中元素的总数。将count()方法与 Java Stream 一起使用的示例如下:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
结论:
5
说明:由于numStream包含 5 个整数元素,因此对其使用count()方法将输出 5。

收集()

collect()方法用于执行流元素的可变缩减。它可用于在处理完成后从流中删除内容。它使用Collector类来执行缩减。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
结论:
[43, 65, 1, 63]
说明:在此示例中,流中的所有奇数元素都被过滤并收集/减少到名为odd 的列表中。最后,打印出奇数列表。

最小值()最大值()

min()方法,顾名思义,可用于流中查找其中的最小元素。类似地,max()方法可用于查找流中的最大元素。让我们尝试通过示例来了解如何使用它们:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
结论:
最小元素:1 最大元素:98
说明:在本例中,我们使用min ()方法 打印 numStream 中的最小元素,并使用max()方法打印最大元素。注意这里,在使用max()方法之前,我们已经再次向numStream中添加了元素。这是因为min()是一个终端操作,会破坏原始流的内容,只返回最终结果(在本例中是整数“最小”)。

findAny()findFirst()

findAny()返回流中的任何元素作为Optional。如果流为空,它还将返回一个可选值,该值将为空。 findFirst()将流的第一个元素作为Optional返回。与findAny()方法一样,如果相应的流为空,则findFirst()方法也会返回一个空的可选参数。我们来看看下面基于这些方法的示例:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
结论:
可选[43] 可选.空
说明:这里,在第一种情况下,findFirst()方法将流的第一个元素作为Optional返回。然后,当线程被重新分配为空线程时,findAny()方法返回一个空的Optional

allMatch()anyMatch()noneMatch()

allMatch()方法用于检查流中的所有元素是否匹配某个谓词,如果匹配则返回布尔值true ,否则返回false。如果流为空,则返回trueanyMatch()方法用于检查流中的任何元素是否与某个谓词匹配。如果是则返回true,否则返回 false。如果流为空,则返回false。如果流中没有元素与谓词匹配,则noneMatch()方法返回true ,否则返回false。说明这一点的示例如下所示:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
结论:
假 真 假
说明:对于包含 1 作为元素的 流numStream , allMatch()方法返回false,因为所有元素都不为 1,但只有一个元素为 1。anyMatch()方法返回true,因为至少有一个元素为 1。noneMatch ()方法返回false,因为 1 实际上作为该流中的一个元素存在。

Java Stream 中的惰性求值

在 Java 8 中使用 Java Streams 时,惰性求值会带来优化。它们主要涉及延迟中间操作,直到遇到终端操作。惰性求值负责防止计算上不必要的资源浪费,直到实际需要结果为止。中间操作产生的输出流仅在终端操作完成后生成。惰性求值适用于 Java 流中的所有中间操作。当处理无限流时,惰性求值的一个非常有用的用途。这样就可以避免很多不必要的处理。

Java Stream 中的管道

Java Stream 中的管道由一个输入流、零个或多个依次排列的中间操作以及最后一个终端操作组成。Java Streams 中的中间操作是惰性执行的。这使得流水线中间操作不可避免。使用管道(基本上是按顺序组合的中间操作),延迟执行成为可能。管道有助于跟踪最终遇到终端操作后需要执行的中间操作。

结论

现在让我们总结一下今天所学到的内容。在本文中:
  1. 我们快速了解了 Java Streams 是什么。
  2. 然后我们学习了许多在 Java 8 中创建 Java 线程的不同技术。
  3. 我们学习了可以在 Java 流上执行的两种主要操作类型(中间操作和终端操作)。
  4. 然后我们详细研究了中间和终端操作的几个示例。
  5. 我们最终了解了有关延迟计算的更多信息,并最终了解了 Java 线程中的管道操作。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION