当前位置: 首页 > news >正文

Kotlin 中的惰性集合

1 通过序列提高效率

首先看以下代码:

val list = listOf(1, 2, 3, 4, 5)
list.filter { it > 2 }.map { it * 2 }

上面的写法很简单,在处理集合时,类似于上面的操作能帮我们解决大部分的问题。但是,当 list 中的元素非常多的时候(比如超过 10 万),上面的操作在处理集合的时候就会显得比较低效。

filter 方法和 map 方法都会返回都会返回一个新的集合,也就是说上面的操作会产生两个临时集合,因为 list 会先调用 filter 方法,然后产生的集合会再次调用 map 方法。如果 list 中的元素非常多,这将是一笔不小的开销。为了解决这个问题,Sequence(序列)就出现了。序列可以避免创建这些临时的中间对象。

以下是序列的使用:

val list = listOf(1, 2, 3, 4, 5)
list.asSequence().filter { it > 2 }.map { it * 2 }.toList()

首先通过 asSequence() 方法将列表转换为一个序列,然后在这个序列上进行相应的操作,最后通过 toList() 方法将序列转换为列表。

将 list 转换为序列,在很大程度上就提高了上面操作集合的效率。这是因此在使用序列的时候,filter 方法和 map 方法的操作都没有创建额外的集合,这样当集合中的元素数量巨大的时候,就减少了大部分开销。

在 Kotlin 中,序列中元素的求值是惰性的,这就意味着在利用序列进行链式求值时,不需要像操作普通集合那样,每进行一次求值操作,就产生一个新的集合保存中间数据。那么惰性又是什么意思呢?

在编程语言理论中,惰性求值(Lazy Evaluation)表示一种在需要时才进行求值的计算方式。在使用惰性求值的时候,表达式不在它被绑定到变量之后就立即求值,而是在该值被取用时才去求值。通过这种方式,不仅能得到性能上的提升,还有一个重要的好处就是它可以构造出一个无限的数据类型。

通过上面的定义我们可以知道惰性求值的两个好处,一个是优化性能,另一个就是能构造出无限的数据类型。

2 序列的操作方式

我们知道序列中元素的求值方式是采用惰性求值的。那么,惰性求值在序列中是如何体现的呢?

以下面的代码为例:

list.asSequence().filter { it > 2 }.map { it * 2 }.toList()

在这个例子中,我们对序列总共执行了两类操作,第一类:

filter { it > 2 }.map { it * 2 }

filter 和 map 的操作返回的都是序列,我们将这类操作称为中间操作。还有一类:

toList()

这一类操作序列转换为 List,我们将这类操作称为末端操作。其实,Kotlin 中序列的操作就分为两类,一类是中间操作,另一类则是末端操作。

2.1 中间操作

在对普通集合进行链式操作的时候,有些操作会产生中间集合,当用这类操作来对序列进行求值的时候,它们就被称为中间操作,比如上面的 filter 和 map。

每一次中间操作返回的都是一个序列,产生的新序列内部知道如何去变换原来序列中的元素。中间操作都是采用惰性求值的, 比如:

list.asSequence().filter {println("filter $it")it > 2
}.map {println("map $it")it * 2
}

执行,没有打印,说明上面的操作中的 println 方法根本就没有执行,这说明 filter 方法和 map 方法的执行被延迟了,这就是惰性求值的体现。

惰性求值仅仅在该值被需要的时候才会真正去求值。那么这个“被需要”的状态该怎么触发呢?这就是另外一个操作了——末端操作。

2.2 末端操作

在对集合进行操作的时候,大部分情况下,我们在意的只是结果,而不是中间过程。

末端操作就是一个返回结果的操作,它的返回值不能是序列,必须是一个明确的结果,比如列表、数字、对象等表意明确的结果。末端操作一般都是放在链式操作的末尾,在执行末端操作的时候,会出发中间操作的延迟计算,也就是将被需要这个状态打开了。

下面给上面的例子加上末端操作:

list.asSequence().filter {println("filter $it")it > 2
}.map {println("map $it")it * 2
}.toList()//filter 1
//filter 2
//filter 3
//map 3
//filter 4
//map 4
//filter 5
//map 5

可以看到,所有的中间操作都被执行了。

如果不用序列而是用列表来实现会有什么不同之处:

list.filter {println("filter $it")it > 2
}.map {println("map $it")it * 2
}.toList()//filter 1
//filter 2
//filter 3
//filter 4
//filter 5
//map 3
//map 4
//map 5

通过对比上面的结果,可以发现,普通集合在进行链式操作的时候会现在 list 上调用 filter,然后产生一个结果列表,接下来 map 就在这个结果列表上进行操作。而序列不一样,序列在执行链式操作时,会将所有的操作都引用在一个元素上,也就是说,第 1 个元素执行完所有的操作之后,第 2 个元素在去执行所有的操作,以此类推。

反映扫上面的这个例子,就是第 1 个元素执行了 filter 之后再去执行 map,然后,第 2 个元素也是这样。通过上面序列的返回结果可以知道,由于列表中的元素 1、2 没有满足 filter 操作中大于 2 的条件,所以接下来的 map 操作就不会去执行了。所以,当我们使用序列的时候,如果 filter 和 map 的位置是可以相互调换的话,应该优先使用 filter,这样会减少一部分开销。

3 序列可以是无限的

惰性求值最大的好处就是可以构造出一个无限的数据类型。

那么我们是否可以使用序列来构造一个无限的数据类型呢?答案是肯定的。常见的无限数据类型是什么呢?数列,比如自然数数列就是一个无限的数列。

那么如何去实现一个自然数列呢?采用一般的列表肯定是不行的,因为构建一个列表必须列举出列表中元素,而我们是么有办法将自然数全部列举出来。

自然数是有一定规律的,就是后一个数永远是前一个数加 1 的结果,我们只需要实现一个列表,让这个列表描述这种规律,那么也就是相当于实现了一个无限的自然数数列。Kotlin 为我们提供了这样一个方法,去创建无限的数列:

val naturalNumList = generateSequence(0) { it + 1 }

通过上面着一行代码,通过调用 generateSequence 就非常简单地实现了自然数数列。

我们知道序列是惰性求值的,所以上面创建的序列是不会把所有的自然数都列举出来的,只有在我们调用一个末端操作的时候,才去列举我们所需要的列表。

比如我们要从这个自然数列表中取出前 10 个自然数:

val list = naturalNumList.takeWhile { it <= 9 }.toList()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

关于无限序列这一点,我们不能将一个无限的数据结构通过穷举的方式呈现出来,而只是实现了一种表示无限的状态,让我们在使用的时候感觉它是无限的。

4 序列与 Java 8 Stream 对比

序列看上去就和 Java 8 中的流(Stream)比较类似。下面就列举一些 Java 8 Stream 中比较常见的特性,并与 Kotlin 中的序列进行比较。

4.1 Java 也能使用函数风格 API

在 Java 8 出来之后,在 Java 中也能像在 Kotlin 中那样操作集合了:

students.stream().filter(it -> it.sex == "m").collect(toList());

在上面的 Java 代码中,我们通过使用 stream 就能够使用类似于 filter 这种简洁的函数式 API 了。

但是相比于 Kotlin,Java 的这种操作方式还是有些繁琐,因为如果要对集合使用这种 API,就必须先将集合转换为 stream,操作完成之后,还要将 stream 转换为 List,这种操作有点类似于 Kotlin 的序列。这是因为 Java 8 的流和 Kotlin 中的序列一样,也是惰性求值的,这就意味着 Java 8 的流也是存在中间操作和末端操作的,所以必须通过上面的一系列转换才行。

4.2 Stream 是一次性的

与 Kotlin 的序列不同,Java 8 中的流是一次性的。意思就是说,如果我们创建了一个 Stream,我们只能在这个 Stream 上遍历一次。这就和迭代器很相似,当我们遍历万之后,这个流就相当于被消费掉了,我们必须再创建一个新的 Stream 才能再遍历一次。

Stream<Student> studentsStream = students.stream();
studentsStream.filter(it -> it.sex == "m").collect(toList());
studentsStream.filter(it -> it.sex == "f").collect(toList());
4.3 Stream 能够并行处理数据

Java 8 中的流非常强大,其中有一个非常重要的特性就是 Java 8 Stream 能够在多核架构上并行的进行流处理。比如将前面的例子转换为并行处理的方式如下:

students.paralleStream().filter(it -> it.sex == "m").collect(toList());

相关文章:

Kotlin 中的惰性集合

1 通过序列提高效率 首先看以下代码&#xff1a; val list listOf(1, 2, 3, 4, 5) list.filter { it > 2 }.map { it * 2 }上面的写法很简单&#xff0c;在处理集合时&#xff0c;类似于上面的操作能帮我们解决大部分的问题。但是&#xff0c;当 list 中的元素非常多的时…...

2024年React初学者入门路线指南

在这篇文章中&#xff0c;我们一步一步探索了如何从零基础开始学习React&#xff0c;并逐渐成长为一名初级开发者。通过理解基础概念、实践构建静态和动态项目&#xff0c;最终发展到创建复杂的应用程序并加入到个人作品集中&#xff0c;您现在已经准备好迈向React开发者的职业…...

【Java基础】了解Java安全体系JCA,使用BouncyCastle的ED25519算法生成密钥对、数据签名

文章目录 一.Java安全体系结构二.JCA和JCE三.CSP(加密服务提供程序)与Engine类1.CSP2.Engine类如何使用引擎类 四.查看当前JDK支持的算法服务提供商(Provider)五.BouncyCastle是什么六.如何使用BouncyCastle&#xff1f;七.bouncycastle实现ED25519工具类 一.Java安全体系结构 …...

SQL Server创建存储过程

使用以下语句创建一个存储过程&#xff1a; CREATE PROCEDURE [schema_name.]procedure_nameparameter1 datatype,parameter2 datatype,... AS BEGIN-- 存储过程的逻辑代码-- 可以包含SQL语句、控制流语句、变量声明等-- 示例&#xff1a;查询表中的数据SELECT column1, colum…...

GraphPad Prism 10:一站式数据分析解决方案

GraphPad Prism 10是一款功能强大的数据分析和可视化软件&#xff0c;广泛应用于生命科学研究、医学、生物、化学等多个领域。以下是对其详细功能的介绍&#xff1a; 首先&#xff0c;GraphPad Prism 10具有出色的数据可视化功能。它支持各种类型的图表和图形&#xff0c;包括…...

前端基础篇-深入了解 Ajax 、Axios

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Ajax 概述 2.0 Axios 概述 3.0 综合案例 1.0 Ajax 概述 通过 Ajax 可以给服务器发送请求&#xff0c;并获取服务器响应的数据。异步交互是指&#xff0c;可以在不…...

是德科技keysight N1912A双通道功率计

181/2461/8938产品概述&#xff1a; Keysight(原Agilent) N1912A P系列双通道功率计可提供峰值、峰均比、平均功率、上升时间、下降时间、最大功率值、最小功率值以及宽带信号的统计数据。 Keysight(原Agilent) N1912A P系列双通道功率计, 可提供峰值、峰均比、平均功率、上升…...

怿星科技Neptune CHT-S测试系统,让智能座舱测试更加高效便捷

随着汽车“智能化”浪潮的推进&#xff0c;汽车的智能化水平正在持续刷新行业认知。在这股智能化潮流中&#xff0c;智能座舱作为客户体验最为直观的部分&#xff0c;其重要性不言而喻。倘若座舱设备出现死机、黑屏、卡顿等现象&#xff0c;都将对客户的使用体验产生非常大的影…...

Vscode初建Vue时几个需要注意的问题

首先放图 注意点1.打开文件夹时&#xff0c;可以是VUE2 或者其他&#xff0c;但不能是VUE&#xff0c;会报错 注意点2.终端输入命令“npm init -y" npm init -y -y 的含义&#xff1a;yes的意思&#xff0c;在init的时候省去了敲回车的步骤&#xff0c;生成的默认的packag…...

最长不下降子序列

问题描述&#xff1a; 统计一个数组中的最长不下降子序列。 示例&#xff1a; 输入&#xff1a;14 输入&#xff1a;13 7 9 16 38 24 37 18 44 19 21 22 63 15 输出&#xff1a;8&#xff08;其中是7 9 16 18 19 21 22 63&#xff09; 方法&#xff1a;借鉴B站UP主T_zhao…...

QT gridlayout 循环设置组件,表格也通用 已解决

在需求中。经常遇到&#xff0c;表格 展示需求。 几乎都是json格式的。 // 列表配置文件QJsonArray listJsonArray getCfgJsonData("details_tab_table_config.json");if (listJsonArray.isEmpty()){return;}ui->gridWidget->setMaximumSize(QSize(310, 180)…...

后端前行Vue之路(一):初识Vue

1.Vue是什么 Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项目整合。另一方…...

C#、.NET版本、Visual Studio版本对应关系及Visual Studio老版本离线包下载地址

0、写这篇文章的目的 由于电脑的环境不同&#xff0c;对于一个老电脑找到一个适配的vscode环境十分不易。总结一下C#、.NET、Visual Studio版本的对应关系&#xff0c;及各个版本Visual Studio的下载地址供大家参考 1、C#、.NET版本、Visual Studio版本对应关系如下 2、Visua…...

yarn安装包时报错error Error: certificate has expired

安装教程&#xff1a; 配置镜像地址&#xff1a; npm config set registry https://registry.npmmirror.com//镜像&#xff1a;https://developer.aliyun.com/mirror/NPM 安装yarn&#xff1a; npm install --global yarn查看版本&#xff1a; yarn --version卸载&#xff…...

手机可以格式化存储卡吗?格式化以后出现什么情况

随着智能手机的普及&#xff0c;存储卡&#xff08;如SD卡、MicroSD卡等&#xff09;已成为手机存储扩展的重要工具。然而&#xff0c;在使用过程中&#xff0c;我们有时可能会遇到需要格式化存储卡的情况。那么&#xff0c;手机能否直接格式化存储卡呢&#xff1f;格式化后存储…...

亚马逊AWS展示高效纠错的全新量子比特!

亚马逊网络服务公司&#xff08;AWS&#xff09;在量子计算的纠错技术领域取得了显著成就&#xff0c;极大地简化了量子系统的复杂性和资源需求。他们的研究人员通过采用“双轨擦除”量子比特&#xff08;dual-rail erasure qubit&#xff09;技术&#xff0c;有效地克服了量子…...

FEX-Emu在Debian/Ubuntu系统使用

FEX-Emu在Debian/Ubuntu系统使用 1. Debootstrap子系统安装&#xff08;可选&#xff09;2. Debian/Ubuntu依赖包安装3. 获取FEX-Emu源码并编译4. 根文件系统RootFS安装5. 基于 FEX-Emu 运行应用 1. Debootstrap子系统安装&#xff08;可选&#xff09; sudo apt-get install …...

docker 不同架构镜像融合问题解决

1、背景 docker 作为目前容器的标准之一&#xff0c;但是对于多种架构的平台的混合编译支撑不是很好。因此衍生了镜像融合&#xff0c;分别将多种不同的架构构建好&#xff0c;然后将镜像进行融合上传。拉取镜像的会根据当前系统的架构拉取不同的镜像&#xff0c;也可以通过 -…...

windows_anaconda 安装pytorch

查看CUDA版本 cmd nvidia-smi # NVIDIA-SMI 546.56 Driver Version: 546.56 CUDA Version: 12.3nvcc --version # nvcc: NVIDIA (R) Cuda compiler driver # Copyright (c) 2005-2023 NVIDIA Corporation # Built on Wed_Nov_22_10:30:42_Pacific_Standard_Time_2023 # C…...

IP SSL证书注册流程

使用IP地址申请SSL证书&#xff0c;需要用公网IP地址申请&#xff0c;申请之前确保直接的IP地址可以开放80或者443端口两者选择1个就好&#xff0c;端口不需要一直开放&#xff0c;只要认证的几分钟内开放就可以了&#xff0c;然后IP地址根目录可以上传txt文件。 IP SSL证书认…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

C++实现分布式网络通信框架RPC(2)——rpc发布端

有了上篇文章的项目的基本知识的了解&#xff0c;现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...

《信号与系统》第 6 章 信号与系统的时域和频域特性

目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...

ArcPy扩展模块的使用(3)

管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如&#xff0c;可以更新、修复或替换图层数据源&#xff0c;修改图层的符号系统&#xff0c;甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...