Java 8流– Java流

时间:2020-02-23 14:36:19  来源:igfitidea点击:

欢迎使用Java 8 Stream API教程。
在Java 8的最后几篇文章中,我们研究了Java 8接口更改和功能接口以及Lambda表达式。
今天,我们将研究Java 8中引入的主要API之一-Java Stream。

Java 8流

  • Java 8流
  • 集合和Java流
  • Java 8 StreamFunction和BiFunction中的功能接口
  • 谓词和双谓词
  • 消费者与消费者
  • 供应商
  • java.util.Optional
  • java.util.Spliterator
  • Java Stream中级和终端操作
  • Java Stream短路操作
  • Java流示例创建Java流
  • 将Java Stream转换为Collection或者Array
  • Java Stream中间操作
  • Java Stream终端操作
  • Java 8 Stream API限制

Java流

在研究Java Stream API示例之前,让我们看看为什么需要它。
假设我们要遍历整数列表,并找出所有大于10的整数之和。

在Java 8之前,实现的方法是:

private static int sumIterator(List<Integer> list) {
	Iterator<Integer> it = list.iterator();
	int sum = 0;
	while (it.hasNext()) {
		int num = it.next();
		if (num > 10) {
			sum += num;
		}
	}
	return sum;
}

上述方法存在三个主要问题:

  • 我们只想知道整数的总和,但我们还必须提供迭代的进行方式,这也称为外部迭代,因为客户端程序正在处理算法以遍历列表。

  • 该程序本质上是顺序执行的,因此我们无法轻松地并行执行此操作。

  • 有很多代码可以完成一个简单的任务。

为了克服上述所有缺点,引入了Java 8 Stream API。
我们可以使用Java Stream API来实现内部迭代,这更好,因为Java框架控制着迭代。

内部迭代提供了一些功能,例如顺序和并行执行,基于给定条件的过滤,映射等。

大多数Java 8 Stream API方法参数都是函数接口,因此lambda表达式可以很好地与它们一起使用。
让我们看看如何使用Java Streams在单行语句中编写以上逻辑。

private static int sumStream(List<Integer> list) {
	return list.stream().filter(i -> i > 10).mapToInt(i -> i).sum();
}

请注意,以上程序利用了Java框架迭代策略,过滤和映射方法,并会提高效率。

首先,我们将研究Java 8 Stream API的核心概念,然后我们将通过一些示例来了解最常用的方法。

集合和Java流

集合是用于保存值的内存数据结构,在开始使用集合之前,应已填充所有值。
而Java Stream是按需计算的数据结构。

Java Stream不存储数据,而是对源数据结构(集合和数组)进行操作,并生成可用于并执行特定操作的流水线数据。
例如,我们可以从列表创建流,并根据条件对其进行过滤。

Java Stream操作使用功能接口,这使其非常适合使用lambda表达式的功能编程。
如您在上面的示例中看到的那样,使用lambda表达式使我们的代码可读性强且简短。

Java 8 Stream内部迭代原理有助于在某些流操作中实现延迟搜索。
例如,可以延迟实施过滤,映射或者重复删除,从而实现更高的性能和优化范围。

Java流是消耗性的,因此无法创建流引用以供将来使用。
由于数据是按需提供的,因此无法多次重复使用同一数据流。

Java 8 Stream支持顺序以及并行处理,并行处理对于实现大型集合的高性能非常有帮助。

所有Java Stream API接口和类都在java.util.stream包中。
由于我们可以在使用自动装箱的集合中长时间使用int等原始数据类型,并且这些操作可能会花费很多时间,因此有一些针对原始类型的特定类-IntStream,LongStream和DoubleStream。

Java 8 Stream中的功能接口

Java 8 Stream API方法中一些常用的功能接口是:

  • Function和BiFunction:Function表示一个函数,该函数接受一种类型的参数并返回另一种类型的参数。
    " Function <T,R>"是通用形式,其中T是函数输入的类型,R是函数结果的类型。
    对于处理原始类型,有特定的Function接口– ToIntFunction, ToLongFunction,ToDoubleFunction,ToIntBiFunction,ToLongBiFunction,ToDoubleBiFunction,LongToIntFunction,LongToDoubleFunction,IntToLongFunction`,IntToDoubleFunction等。
    使用的是:<R> Stream <R> map(Function <?super T,?extended R> mapper)

  • IntStream mapToInt(ToIntFunction <?super T>映射器)–类似地,对于长且双返回的原始特定流。

  • IntStream flatMapToInt(Function <?super T,?extends IntStream> mapper)–类似地,对于long和double

  • <A> A [] toArray(IntFunction <A []>生成器)

  • <U> U reduce(U标识,BiFunction <U,?super T,U>累加器,BinaryOperator <U>组合器)

  • 谓词和BiPredicate:它表示针对流元素进行测试的谓词。
    这用于从java流中过滤元素。
    与Function一样,int,long和double也有原始的特定接口。
    使用Predicate或者BiPredicate专门化的Stream方法有:Stream <T> filter(Predicate <?super T> predicate)

  • boolean anyMatch(Predicate <?super T>谓词)

  • boolean allMatch(Predicate <?super T>谓词)

  • 布尔值noneMatch(Predicate <?super T>谓词)

  • 消费者和BiConsumer:它表示一个接受单个输入参数且不返回结果的操作。
    它可以用于对Java流的所有元素执行某些操作。
    使用ConsumerBiConsumer或者原始专业化接口的Java 8 Stream方法包括:Stream <T> peek(Consumer <?超级T>动作)

  • void forEach(Consumer <?super T>操作)

  • forEachOrdered(Consumer <?super T>操作)无效

  • 供应商:供应商代表一项操作,通过该操作我们可以在流中生成新值。
    Stream中带有" Supplier"参数的一些方法是:public static <T> Stream <T> generate(Supplier <T> s)

  • <R> R收集(供应商<R>供应商,BiConsumer <R ,?超级T>累加器,BiConsumer <R,R>组合器)

java.util.Optional

Java Optional是一个容器对象,可以包含也可以不包含非null值。
如果存在值,isPresent()将返回true,而get()将返回该值。
流终端操作返回Optional对象。
其中一些方法是:

  • 可选的<T> reduce(BinaryOperator <T>累加器)
  • 可选的<T> min(比较器<?super T>比较器)
  • 可选的<T> max(Comparator <?super T>比较器)
  • 可选的<T> findFirst()
  • 可选的<T> findAny()

java.util.Spliterator

为了支持Java 8 Stream API中的并行执行,使用了" Spliterator"接口。
Spliterator的trySplit方法返回一个新的Spliterator,它管理原始Spliterator元素的子集。

Java Stream中级和终端操作

返回新Stream的Java Stream API操作称为中间操作。
在大多数情况下,这些操作本质上都是惰性的,因此它们开始生成新的流元素并将其发送到下一个操作。
中间操作绝不是最终结果生成操作。
常用的中间操作是" filter"和" map"。

返回结果或者产生副作用的Java 8 Stream API操作。
一旦在流上调用了终端方法,它将消耗该流,此后我们将无法使用流。
终端操作本质上是急切的,即它们在返回结果之前处理流中的所有元素。
常用的终端方法有:forEach,toArray,min,max,findFirst,anyMatch,allMatch等。
您可以从返回类型中识别终端方法,它们永远不会返回流。

Java Stream短路操作

如果中间操作可能产生无限流,则称为短路。
例如,limit()和skip()是两个短路中间操作。

如果终端操作可能在无限时间内终止于无限流,则该操作称为短路。
例如,anyMatch,allMatch,noneMatch,findFirst和findAny是短路的终端操作。

Java Stream示例

我已经介绍了Java 8 Stream API的几乎所有重要部分。
使用这项新的API功能令人兴奋,让我们在一些Java流示例中看到它的实际效果。

创建Java流

我们可以通过几种方法从数组和集合创建Java流。
让我们用简单的例子来研究它们。

  • 我们可以使用Stream.of()从相似类型的数据创建一个流。
    例如,我们可以从一组int或者Integer对象创建整数的Java流。

  • 我们可以将Stream.of()与Objects数组一起使用来返回流。
    请注意,它不支持自动装箱,因此我们无法传递基本类型数组。

  • 我们可以使用Collectionstream()创建顺序流,并使用parallelStream()创建并行流。

  • 我们可以使用Stream.generate()和Stream.iterate()方法来创建Stream。

  • 使用" Arrays.stream()"和" String.chars()"方法。

将Java Stream转换为Collection或者Array

有几种方法可以从Java Stream获取Collection或者Array。

  • 我们可以使用java Stream的collect()方法从流中获取List,Map或者Set。

  • 我们可以使用流toArray()方法从流中创建一个数组。

Java Stream中间操作

让我们看一下常用的Java Stream中间操作示例。

  • 流filter()示例:我们可以使用filter()方法测试条件的流元素并生成过滤列表。

  • 流map()示例:我们可以使用map()将函数应用于流。
    让我们看看如何使用它将大写函数应用于字符串列表。

  • 流sorted()示例:我们可以使用sorted()通过传递Comparator参数来对流元素进行排序。

  • 流flatMap()示例:我们可以使用flatMap()从列表流创建流。
    让我们看一个简单的例子来消除这个疑问。

Java Stream终端操作

让我们看一些Java流终端操作示例。

  • 流reduce()示例:我们可以使用reduce()通过关联累加函数对流的元素进行归约,然后返回Optional。
    让我们看看如何使用它将流中的整数相乘。

  • Stream count()示例:我们可以使用此终端操作来计算流中的项目数。

  • Stream forEach()示例:这可用于迭代流。
    我们可以用它代替迭代器。
    让我们看看如何使用它来打印流中的所有元素。

  • Stream match()示例:让我们看一些Stream API中匹配方法的示例。

  • Stream findFirst()示例:这是一个短路的终端操作,让我们看看如何使用它从D开头的流中查找第一个字符串。

Java 8 Stream API限制

Java 8 Stream API带来了许多与列表和数组一起使用的新东西,但是它也有一些限制。

  • 无状态Lambda表达式:如果您使用并行流,并且Lambda表达式是有状态的,则可能导致随机响应。
    让我们用一个简单的程序查看它。
    StatefulParallelStream.java
    如果我们在上述程序上运行,您将获得不同的结果,因为它取决于流被迭代的方式,并且我们没有为并行处理定义任何顺序。
    如果使用顺序流,则不会出现此问题。

  • 流被使用后,以后将无法使用。
    如您在上面的示例中看到的,每次创建流时。

  • Stream API中有很多方法,最令人困惑的部分是重载方法。
    它使学习曲线花费时间。