1. 概述

在这个简短的教程中,我们将快速了解Java流中的中级和终止操作,创建空流的方法,以及如何检查空流。

2. 流与流操作

Java 8的特性之一是流(Stream)API。流是一系列元素,我们可以对其进行迭代并执行操作。

流操作主要分为两种类型——中级和终端。 中级和终端操作可以串联在一起形成流管道。

终端操作顾名思义,位于流管道的末尾,返回结果,如distinct(),或产生副作用,如forEach()

另一方面,中级操作如sorted()会将流转换为另一个流。因此,我们可以链式多个中级操作。

任何终端或中级操作都不会实际改变流的源头,而是产生结果。 同时,中级操作以懒惰的方式进行,只有在启动终端操作后才会执行计算。

让我们看一个例子:

Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6);

int sumOfEvenNumbers = numbers
  .filter(number -> number%2 == 0)
  .reduce(0, Integer::sum);

Assert.assertEquals(sumOfEvenNumbers, 12);

这里我们创建了一个整数流。我们使用了中级操作filter(),它创建了一个偶数流。最后,我们使用了终端操作reduce()来获取所有偶数的总和。

3. 在Java中创建空流

有时,我们可能需要将流作为参数传递给方法,或者从方法返回流。空流对于处理NullPointerException很有用。 此外,一些流操作,如findFirst(), findAny(), min(), 和 max(),会在检查流是否为空后返回相应结果。

创建流有多种方式,因此创建空流的方法也有多样性。

首先,我们可以直接使用Stream类的empty()方法:

Stream<String> emptyStream = Stream.empty();

empty()方法返回一个空的顺序字符串流。

我们也可以使用of()方法创建任何类型的空流。of()方法返回一个有序的序列流,包含传给它的参数作为元素。如果没有传入参数,它将返回一个空流:

Stream<String> emptyStr = Stream.of();

同样,我们可以使用IntStream创建基本类型的流:

IntStream intStream = IntStream.of(new int[]{});

Arrays类有一个方法stream(),接受数组作为参数并返回与参数类型相同的流。我们可以使用这个方法通过传入一个空数组来创建一个空流:

IntStream emptyIntStream = Arrays.stream(new int[]{});

最后,我们可以使用ListSet等集合的stream()方法创建空流。空集合将创建一个空流:

Stream<Integer> emptyIntStream = new ArrayList<Integer>().stream();

4. 检查空流

我们可以通过使用诸如findFirst()findAny()等短路终端操作来检查空流:

Stream<String> emptyStream = Stream.empty();
assertTrue(emptyStream.findAny().isEmpty());

这里,findFirst()如果流为空则返回一个空的Optional。然后我们检查Optional中是否存在值。由于流为空,Optional中没有值,它返回false

然而,我们必须记住,我们只能对流进行一次操作。如果我们重用流,可能会遇到IllegalStateException

IllegalStateException: stream has already been operated upon or closed.

因此,我们只允许执行消耗流的操作一次。如果我们想重用流,必须处理这个IllegalStateException

为了解决这个问题,每当需要检查流是否为空时,我们可以使用Supplier函数接口创建一个新的流:

Supplier<Stream<Integer>> streamSupplier = Stream::of;

Optional<Integer> result1 = streamSupplier.get().findAny();
assertTrue(result1.isEmpty());
Optional<Integer> result2 = streamSupplier.get().findFirst();
assertTrue(result2.isEmpty());

在这里,我们首先定义了一个空流,然后创建了一个streamSupplier对象,类型为Stream<Integer>。这样,每次调用get()方法都会返回一个新的空流对象,我们可以安全地在其上执行另一个流操作。

5. 总结

在这篇简短的文章中,我们了解了一些在Java中创建空流的方法。我们还探索了如何检查流是否为空,以及如何在避免因流已关闭或已被操作而抛出的著名IllegalStateException的情况下多次重用流。

如往常一样,本文的源代码可以在GitHub上找到。