java collection api_Java Stream和Collection比较:何时以及如何从Java API返回?

向您展示一些可以非常方便地使用Java Stream流的场景以及如何使用它们的示例。

本文基于标准Java库java.util.stream。它既与反应流无关,也与诸如Vavr之类的其他流实现无关。另外,我将不介绍诸如并行执行之类的流的高级细节。

首先,让我们简要讨论与集合相比独特的流功能。尽管存在一些相似之处,但差异是很大的,您不应将流仅视为库中的另一种集合。

根据java.util.stream 的文档,最重要的功能是:没有存储空间,可能是无限制的 -集合是现成的数据结构,而流表示产生数据的能力,通常在创建流时甚至不存在。由于不存储流中的数据,因此我们可以创建几乎不确定的流,或者可以更实际地对其重新措辞,我们可以让消费者决定要从流中读取多少个元素,从生产者的角度来看,它可能是不确定的(例如new Random().ints())。

懒惰加载 —在定义流时暂停许多操作(例如过滤,映射),并且仅在使用者决定使用流中的数据时才执行

本质上是实用的 -由于您已经具有使用流的经验,因此您可能会注意到处理流中的数据是为每个步骤(例如过滤器或映射)创建新流,而不是修改源数据

消耗性 -您只能读取一次流,然后与可以多次读取的集合不同,它变为“消耗性”

现在让我们看看我们可以用流解决什么问题。

处理大量数据

假设,我们必须将数据从外部服务复制到我们的数据库中。要复制的数据量可以任意大。我们无法获取所有数据,无法将其存储在一个集合中,然后保存在数据库中,因为这可能会耗尽堆内存。我们必须分批处理数据,并设计外部服务客户端和数据库存储之间的接口。由于流不存储日期,因此可以使用它安全地处理所需的数据量。

在示例(及以下所有示例)中,我们将使用java.util.stream.Stream接口的静态方法来构建流。用Java构建流的最强大,最灵活的方法是实现Spliterator接口,然后使用StreamSupport类将其包装为流。但是,正如我们所看到的,Stream在许多情况下,接口中的静态工厂方法就足够了。

假定一个简单的API从支持分页的外部服务(例如,REST服务,数据库)中获取数据。该API最多可limit从提取项目offset。迭代地使用API​​,我们可以根据需要获取尽可能多的数据

interface ExternalService {

List fetch(int offset, int limit);

}

现在,我们可以使用API​​提供数据流,并将API的使用者与分页API隔离开:

class Service {

private final ExternalService externalService;

public Stream stream(int size, int batchSize) {

var cursor = new Cursor();

return Stream

.generate(() -> next(cursor, size, batchSize))

.takeWhile(not(List::isEmpty))

.flatMap(List::stream);

}private List next(Cursor cursor, int size, int batchSize){

var fetchSize = Math.min(size - cursor.offset, batchSize);

var result = externalService.fetch(cursor.offset, fetchSize);

cursor.inc(result.size());

return result;

}

}

Cursor 握有当前偏移量offset:

private static class Cursor {

private int offset;

void inc(int by) {

offset += by;

}

我们使用Stream.generate()方法构建无限流,其中每个元素由流提供者创建。流元素是从REST API获取的页面List。将为每个流创建Cursor类的实例,以跟踪获取的元素的进度。

Stream.takeWhile()方法用于检测的最后一页,最后返回的数据流T,而不是List。

我们使用flatMap扁平化流。尽管在某些情况下,保留批处理(例如将整个页面保存在一个事务中)可能很有用。

现在,我们可以使用Service.stream(size, batchSize)来检索任意长流,而无需任何分页API的知识(我们决定公开batchSize参数,但这是一个设计决策)。在任何时间点,内存消耗都受到批处理大小的限制。使用者可以一一处理数据,将其保存在数据库中,或者再次进行批处理(批处理大小可能不同)。

快速访问(不完整)数据

假设我们有一个耗时的操作,必须对数据的每个元素执行该操作,并且计算要花费时间t。对于n元素,使用者必须等待t * n才能接收到计算结果。例如,如果用户正在等待带有计算结果的表,则可能是一个问题。我们希望在显示第一结果时立即显示它们,而不是等待所有结果的计算并立即提交表。

public class Producer1 {

private Stream buildStream() {

return Stream.of("a", "b", "c"); }private String expensiveStringDoubler(String input){

return input + input;

}public Stream stream(){

return buildStream().map(this::expensiveComputation);

}

}

消费者:

stream().forEach(System.out::println)

输出:

Processing of: a aa Processing of: b …

输出:

Processing of: a aa Processing of: b …

如我们所见,在开始处理下一个元素之前,用户可以使用第一个元素“ aa ”的处理结果,但是计算仍然是流的生产者责任。换句话说,消费者决定何时以及是否应该执行计算,但是生产者仍然负责如何执行计算。

您可能会认为这很容易,并且不需要流。当然,您是对的,让我们看一下:

public class Producer1Classic {

public List data() {

return List.of("a", "b", "c", "d", "e", "f"); }public String expensiveStringDoubler(String input){

return input + input;

}

}

消费者:

var producer = new Producer1Classic();

for (String element : producer.data()) {

System.out.println(producer.expensiveComputation(element));

}

同样的效果,但是实际上我们已经重新发明了轮子,我们的实现模仿了stream的祖先- Iterator并且我们失去了stream的API的优势。

避免过早计算

再次假设我们要对每个流元素执行耗时的操作。在某些情况下,API的使用者无法提前说出需要多少数据。例如:用户取消了数据加载

在数据处理过程中发生错误,无需处理其余数据

消费者读取数据直到满足条件,例如第一个正值

由于流的惰性,在这种情况下可以避免一些计算。

private Stream buildStream() {

return new Random().doubles().boxed();

}private Double expensiveComputation(Double input){

return input / 2;

}public Stream stream(){

return buildStream().map(this::expensiveComputation);

}

消费者:

stream().peek(System.out::println).filter(value -> value > 0.4).findFirst();

在该示例中,使用者读取数据,直到该值大于0.4。生产者并不了解消费者的这种逻辑,但它只计算必要的项目。逻辑(例如条件)可以在用户端独立更改。

API易于使用

使用流而不是自定义API设计还有另一个原因。流是标准库的一部分,并为许多开发人员所熟知。在我们的API中使用流使其他开发人员更容易使用该API。

其他注意事项

错误处理

传统的错误处理不适用于Streams。由于实际处理将推迟到需要时进行,因此构造流时不会引发异常。基本上,我们有两个选择:引发RuntimeException-终止方法(例如forEach)将引发异常

将元素包装到一个对象中,该对象表示正在处理的元素的当前状态,例如TryVavr库中的特殊类(博客中的详细信息)

资源管理

有时我们必须使用一种资源来提供流数据(例如,外部服务中的会话),并且我们想在流处理完成时将其释放。幸运的是,流实现了Autoclosable接口,我们可以在try-with-resources语句中使用流,从​​而使资源管理变得非常容易。我们要做的就是使用onClose方法在流中注册一个钩子。当流关闭时,该挂钩将自动被调用。

private Stream buildStream() {

return new Random().doubles().boxed();

}private Double expensiveComputation(Double input){

if (input > 0.8) throw new RuntimeException("Data processing exception"); return input / 2;

}public Stream stream(){

return buildStream().map(this::expensiveComputation).onClose(()-> System.out.println("Releasing resources…

消费者:

try (Stream stream = stream()){

stream.forEach(System.out::println);

}

输出:

0.2264004802916616

0.32777949557515484

Releasing resources…

Exception in thread “main” java.lang.RuntimeException: Data processing exception

在该示例中,当发生数据处理异常时,流将通过try-with-resources语句自动关闭,并调用已注册的处理程序。在示例输出中,我们可以看到Releasing resources…处理程序打印的消息。

总结流不是集合。

流可以帮助我们解决以下问题:*处理大量数据*快速访问(不完整的)数据*避免过早计算

构建流并不难。

我们必须注意错误处理。

支持资源管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/267394.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

依赖属性

项目的WF中用到了依赖属性, 有点晕, 不明白, 先来段代码: public static DependencyProperty IsSignInProperty DependencyProperty.Register("IsSignIn", typeof(System.String), typeof(StateMachineWF.WF1)); [DesignerSerializationVisibilityAttribute(Designe…

[UE4]集合:TSet容器

一、TSet<T>是什么 UE4中&#xff0c;除了TArray动态数组外&#xff0c;还提供了各种各样的模板容器。这一节&#xff0c;我们就介绍集合容器——TSet<T>。类似于TArray<T>&#xff0c;尖括号里面的T是模板类型&#xff0c;可以是任何C类型。一个集合表示了一…

【汇总】flash单个文件上传

之前有朋友给我发送email&#xff0c;询问我是否有单个文件上传的源代码&#xff0c;因为当时写这个好像是在09年&#xff0c;所以放哪了一时也没找着。后来整理硬盘的时候&#xff0c;找到了源码&#xff0c;所以决定来个汇总&#xff08;之前写过的关于flashjs上传文件的例子…

2018.3.24 struct

好了今天听完了struct&#xff0c;感觉也差不多了&#xff0c;后面的视频不想听了&#xff0c;io啊预处理啊什么的用时候现学就好了。主要是就这么光听却没有作业可做真的有点不爽。 明天开始15-213&#xff0c;反正手头也有c primer plus了&#xff0c;后面遇到什么问题看书就…

一直苦于没有好的资产管理软件,GLPI能解决吗?

一直苦于没有好的资产管理软件&#xff0c;正好看到网上文章有介绍glpi资产管理开源软件 在此做个记录&#xff0c;有时间一定要测试一下 &#xff08;1&#xff09;资产管理工具GLPI 官网 http://www.glpi-project.org/ GLPI是法语Gestionnaire libre de parc informatique的…

weka的java环境配置_windows下安装和配置Weka

Weka是一款免费的&#xff0c;非商业化的&#xff0c;基于java环境下的开源的机器学习以及数据挖掘软件。Weka里含有各种数据挖掘工具&#xff1a;数据预处理&#xff0c;分类与回归&#xff0c;聚类&#xff0c;关联规则和可视化工具。一、安装weka我们首先需要到weka官网上下…

Windows部署服务WDS实例

一&#xff1a;概述<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />Windows&#xff08;Windows Deployment Services&#xff09; 部署服务适用与大中型网络中的计算机操作系统部署。可以使用 Windows 部署服务来管理映像以及无…

JAVA----爬虫(一)JSoup

jsoup 是一款Java 的HTML解析器&#xff0c;可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API&#xff0c;可通过DOM&#xff0c;CSS以及类似于jQuery的操作方法来取出和操作数据。 官方api:https://jsoup.org/ 一、jsoup功能 简单的例子&#xff1a;抓取wiki的…

java语言模拟_Java语言模拟操作系统.doc

河北大学2010级操作系统课程设计论文PAGEPAGE 27装订线装订线(指导教师用表)学 生 姓 名指 导 教 师论文(设计)题目Java语言模拟操作系统主要研究(设计)内容使用java语言&#xff0c;采用多到程序设计方法基本上实现并模拟了单用户操作系统。该操作系统包括四部分内容&#xff…

极速理解设计模式系列:22.状态模式(State Pattern)

四个角色&#xff1a;抽象状态类(State)、具体状态类(ConcreateState)、情景类(Context)、客户端(Client) 抽象状态类(State):提供一个与情景类有关的State行为。 具体状态类(ConcreateState):实现这个行为&#xff0c;实现一个状态。 情景类(Context):维护一个State的实例对象…

485. Max Consecutive Ones

原题链接&#xff1a;https://leetcode.com/problems/max-consecutive-ones/description/ 这道题目级别为easy&#xff0c;实际做起来也是so easy&#xff1a; /*** Created by clearbug on 2018/2/26.*/ public class Solution {public static void main(String[] args) {Solu…

[转]extern使用方法总结

Extern的问题在于不知道这个关键词出现的时候到底是声明还是定义。谨记&#xff1a;声明可以多次&#xff0c;定义只能一次。在使用中&#xff0c;要形成一种风格。 函数的声明extern关键词是可有可无的&#xff0c;因为函数本身不加修饰的话就是extern的。但是引用的时候一样是…

java 设置pdf 编码格式_Java如何设置PDF文档背景色详解

前言一般生成的PDF文档默认的文档底色为白色&#xff0c;我们可以通过一定方法来更改文档的背景色&#xff0c;以达到文档美化以及保护双眼的作用。 以下内容提供了Java编程来设置PDF背景色的方法。包括&#xff1a;设置纯色背景设置图片背景使用工具Spire.PDF for Java 2.0.3J…

关于strassen矩阵乘法的矩阵大小不是2^k的形式时,时间复杂度是否还是比朴素算法好的看法...

原来是n&#xff0c;找到大于等于n且是2^k形式的数m。n*n的矩阵补全为m*m的矩阵&#xff0c;原来的矩阵放在最左上方&#xff0c;其它位置的值为0.朴素方法&#xff1a;n^3现在&#xff1a;m^2.8即m/n需小于e^(3/2.8)2.919才能好&#xff0c;而n<m<2*n&#xff0c;即使用…

UtilSession failed: Prerequisite check CheckSystemSpace space(22288172004) is not availa

如果你在使用OPatch打11.2 GI/CRS上的PSU时遇到了如上错误信息"UtilSession failed: Prerequisite check "CheckSystemSpace" space"则说明你的CRS_HOME所在文件系统的空闲空间不足22g&#xff0c;这会导致OPatch预检测无法通过。 UTIL session.Oracle…

数据库 oracle 设计三范式

一&#xff1a;表中的数据不能重复&#xff0c;每个字段不可再分。2&#xff1a; 建立在第一范式上&#xff0c;表中的非主键字段必须全部依赖主键&#xff0c;不能部分依赖主键3 建立在第二范式基础上的&#xff0c;非主键字段不能传递依赖于主键字段。转载于:https://www.cnb…

java websocket高并发测试_Websocket全讲解。跨平台的通讯协议 !!基于websocket的高并发即时通讯服务器开发。...

本博文&#xff0c;保证不用装B的话语和太多专业的语言&#xff0c;保证简单易懂&#xff0c;只要懂JAVAEE开发的人都可以看懂。 本博文发表目的是&#xff0c;目前网上针对Websocket的资料太散乱&#xff0c;导致初学者的知识体系零零散散&#xff0c;学习困难加大。本博加以整…

C#实现文件下载代码

提供个C#实现文件下载代码  一&#xff0e;概述&#xff1a; 本文通过一个实例向大家介绍用Visual C#进行Internet通讯编程的一些基本知识。我们知道.Net类包含了请求/响应层、应用协议层、传输层等层次。在本程序中&#xff0c;我们运用了位于请求/响应层的WebRequest类以及W…

Cookie 与Session 的区别

Cookie 与Session 的区别&#xff08;转载&#xff09; 原地址: http://www.cnblogs.com/shiyangxt/archive/2008/10/07/1305506.html 两个都可以用来存私密的东西&#xff0c;同样也都有有效期的说法。 区别在于&#xff1a;session是放在服务器上的&#xff0c;过期与否取决于…

voyage java_GitHub - yezilong9/voyage: 采用Java实现的基于netty轻量的高性能分布式RPC服务框架...

VoyageOverview采用Java实现的基于netty轻量的高性能分布式RPC服务框架。实现了RPC的基本功能&#xff0c;开发者也可以自定义扩展&#xff0c;简单&#xff0c;易用&#xff0c;高效。Features服务端支持注解配置客户端实现Filter机制&#xff0c;可以自定义Filter基于netty3.…