Java 8流– Java流
欢迎使用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流的所有元素执行某些操作。
使用Consumer
,BiConsumer
或者原始专业化接口的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数组一起使用来返回流。
请注意,它不支持自动装箱,因此我们无法传递基本类型数组。我们可以使用Collection
stream()
创建顺序流,并使用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中有很多方法,最令人困惑的部分是重载方法。
它使学习曲线花费时间。