vavr Java的函数式编程神器-Part1

news2024/12/23 19:07:35

微信公众号:阿俊的学习记录空间
小红书:ArnoZhang
wordpress:arnozhang1994
博客园:arnozhang
CSDN:ArnoZhang1994

1. 介绍

Vavr(前称 Javaslang)是一个为 Java 8+ 提供的函数式库,提供持久数据类型和函数控制结构。

1.1. Vavr 中的 Java 8 函数式数据结构

Java 8 的 lambda(λ)使我们能够创建出色的 API,大大增强了语言的表现力。Vavr 利用 lambda 创建了许多基于函数式模式的新特性,其中之一是一个旨在替代 Java 标准集合的函数集合库。

Vavr 集合

LOGO

(这只是鸟瞰图,下面会有可读版本。)

1.2. 函数式编程

在深入数据结构的细节之前,我想先讨论一些基础知识。这将明确我为何创建 Vavr,尤其是新的 Java 集合。

1.2.1. 副作用

Java 应用程序通常充满副作用。它们会改变某种状态,可能是外部世界。常见的副作用包括在原地更改对象或变量、打印到控制台、写入日志文件或数据库。如果副作用以不希望的方式影响程序的语义,则被视为有害。

例如,如果一个函数抛出异常并对此进行解释,那么这被视为影响程序的副作用。此外,异常类似于非局部的 goto 语句,它们打破正常的控制流。然而,现实世界的应用确实会执行副作用。

int divide(int dividend, int divisor) {
    // 如果除数为零则抛出异常
    return dividend / divisor;
}

在函数式环境中,我们处于一个有利的局面,可以在 Try 中封装副作用:

// = Success(result) 或 Failure(exception)
Try<Integer> divide(Integer dividend, Integer divisor) {
    return Try.of(() -> dividend / divisor);
}

这个版本的 divide 不再抛出任何异常。我们通过使用类型 Try 使可能的失败变得明确。

1.2.2. 引用透明性

如果可以用其值替换调用而不影响程序的行为,则称一个函数或更一般的表达式为引用透明。简单来说,给定相同的输入,输出总是相同的。

// 不是引用透明的
Math.random();

// 引用透明的
Math.max(1, 2);

如果所有相关表达式都是引用透明的,则该函数被称为纯函数。由纯函数组成的应用程序只要编译就很可能能正常工作。我们能够对此进行推理。单元测试易于编写,调试变成了过去的遗物。

1.2.3. 价值思维

Clojure 的创造者 Rich Hickey 进行了关于“价值的价值”的精彩演讲。最有趣的值是不可变值。主要原因是不可变值

  • 本质上是线程安全的,因此不需要同步
  • 在 equals 和 hashCode 方面是稳定的,因此是可靠的哈希键
  • 不需要克隆
  • 在未经检查的协变转换中表现出类型安全(Java 特有)

更好的 Java 的关键是使用不可变值配合引用透明的函数。Vavr 提供了必要的控制和集合,以实现这一目标,在日常 Java 编程中。

1.3. 数据结构概述

Vavr 的集合库由一组丰富的基于 lambda 的函数式数据结构组成。它们与 Java 原始集合唯一共享的接口是 Iterable。主要原因是 Java 集合接口的修改方法不返回底层集合类型的对象。

我们将通过查看不同类型的数据结构来了解这一点为何至关重要。

1.3.1. 可变数据结构

Java 是一种面向对象的编程语言。我们将状态封装在对象中,以实现数据隐藏,并提供修改方法来控制状态。Java 集合框架(JCF)建立在这个思想之上。

interface Collection<E> {
    // 从该集合中删除所有元素
    void clear();
}

今天,我把 void 返回类型视为一种气味。这是副作用发生的证据,状态被改变。共享可变状态是失败的重要来源,不仅在并发环境中。

1.3.2. 不可变数据结构

不可变数据结构在创建后不能被修改。在 Java 的上下文中,它们通常以集合包装器的形式使用。

List<String> list = Collections.unmodifiableList(otherList);

// 爆炸!

list.add("why not?");

有各种库为我们提供类似的实用方法。结果总是特定集合的不可修改视图。通常在调用修改方法时会在运行时抛出异常。

1.3.3. 持久数据结构

持久数据结构在被修改时保留其之前的版本,因此实际上是不可变的。完全持久的数据结构允许对任何版本进行更新和查询。

许多操作只执行小的变化。仅仅复制之前的版本效率不高。为了节省时间和内存,识别两个版本之间的相似性并尽可能共享数据至关重要。

此模型不强加任何实现细节。函数式数据结构在此发挥作用。

1.4. 函数式数据结构

也称为纯函数数据结构,这些是不可变且持久的。函数式数据结构的方法是引用透明的。

Vavr 具有广泛使用的函数式数据结构。以下示例将深入解释。

1.4.1. 链表

最流行且最简单的函数式数据结构之一是(单向)链表。它有一个头元素和一个尾链表。链表的行为类似于遵循后进先出(LIFO)方法的堆栈。

在 Vavr 中,我们这样实例化一个链表:

// = List(1, 2, 3)
List<Integer> list1 = List.of(1, 2, 3);

每个链表元素形成一个独立的链表节点。最后一个元素的尾部是 Nil,表示空链表。

LOGO

这使我们能够在不同版本的链表中共享元素。

// = List(0, 2, 3)
List<Integer> list2 = list1.tail().prepend(0);

新头元素 0 链接到原链表的尾部。原链表保持不变。

LOGO

这些操作在常量时间内进行,换句话说,它们与链表大小无关。其他大多数操作需要线性时间。在 Vavr 中,这通过接口 LinearSeq 表达,我们可能已经在 Scala 中了解过。

如果我们需要在常量时间内可查询的数据结构,Vavr 提供 Array 和 Vector。两者均具有随机访问能力。

Array 类型由一个 Java 对象数组支持。插入和删除操作需要线性时间。Vector 在 Array 和 List 之间表现良好,既适用于随机访问,也适用于修改。

实际上,链表也可以用于实现队列数据结构。

1.4.2. 队列

基于两个链表可以实现非常高效的函数式队列。前面的链表保存被出队的元素,后面的链表保存入队的元素。两个操作入队和出队的时间复杂度均为 O(1)。

Queue<Integer> queue = Queue.of(1, 2, 3)
                            .enqueue(4)
                            .enqueue(5);

初始队列由三个元素创建。在后链表上入队两个元素。

LOGO

如果在出队时前链表没有元素,后链表会被反转并成为新的前链表。

LOGO

出队一个元素时,我们得到一个第一个元素和剩余队列的配对。必须返回队列的新版本,因为函数式数据结构是不可变且持久的。原队列不受影响。

Queue<Integer> queue = Queue.of(1, 2, 3);

// = (1, Queue(2, 3))
Tuple2<Integer, Queue<Integer>> dequeued =
        queue.dequeue();

当队列为空时,会抛出 NoSuchElementException。以函数式方式处理时,我们更希望得到一个可选结果。

// = Some((1, Queue()))
Queue.of(1).dequeueOption();

// = None
Queue.empty().dequeueOption();

可选结果可以进一步处理,无论其是否为空。

// = Queue(1)
Queue<Integer> queue = Queue.of(1);

// = Some((1, Queue()))
Option<Tuple2<Integer, Queue<Integer>>> dequeued =
        queue.dequeueOption();

// = Some(1)
Option<Integer> element = dequeued.map(Tuple2::_1);

// = Some(Queue())
Option<Queue<Integer>> remaining =
        dequeued.map(Tuple2::_2);

1.4.3. 排序集合

排序集合是比队列更常用的数据结构。我们使用二叉搜索树以函数式方式对其建模。这些树由每个节点最多有两个子节点和值构成。

我们在有顺序的情况下构建二叉搜索树,由元素比较器表示。任何给定节点的左子树的所有值严格小于

该节点的值,右子树的所有值严格大于该节点。

// = TreeSet(1, 2, 3, 4, 6, 7, 8)
SortedSet<Integer> xs = TreeSet.of(6, 1, 3, 2, 4, 7, 8);

LOGO

对这些树的搜索运行时间为 O(log n)。我们从根节点开始搜索,判断是否找到该元素。由于值的完全排序,我们知道接下来在哪一侧的分支上搜索。

// = TreeSet(1, 2, 3);
SortedSet<Integer> set = TreeSet.of(2, 3, 1, 2);

// = TreeSet(3, 2, 1);
Comparator<Integer> c = (a, b) -> b - a;
SortedSet<Integer> reversed = TreeSet.of(c, 2, 3, 1, 2);

大多数树操作本质上是递归的。插入函数的行为类似于搜索函数。当达到搜索路径的末端时,创建一个新节点,并重新构建整个路径直至根节点。尽可能引用现有子节点。因此,插入操作的时间和空间复杂度均为 O(log n)。

// = TreeSet(1, 2, 3, 4, 5, 6, 7, 8)
SortedSet<Integer> ys = xs.add(5);

LOGO

为了保持二叉搜索树的性能特征,树必须保持平衡。从根到叶子的所有路径必须具有大致相同的长度。

在 Vavr 中,我们基于红黑树实现了二叉搜索树。它使用特定的着色策略来保持插入和删除时树的平衡。有关此主题的更多信息,请参阅 Chris Okasaki 的书《Purely Functional Data Structures》。

### 1.4.4. HashMap

HashMap 是一种常用的不可变数据结构,用于存储键值对。Vavr 的 HashMap 是基于持久性和不可变性构建的。它允许对键值对进行高效的插入、删除和查找操作。

在 Vavr 中,我们可以这样创建一个 HashMap:

```java
// = HashMap(1 -> "one", 2 -> "two")
HashMap<Integer, String> map = HashMap.of(1, "one", 2, "two");

每次插入或删除操作都会返回一个新的 HashMap 实例,原始 HashMap 不会被修改。

1.4.5. 其他函数式数据结构

除了上面提到的链表、队列和 HashMap,Vavr 还提供了其他多种常用的函数式数据结构,包括:

  • :如 TreeSet 和 TreeMap,它们提供了基于排序的集合和映射。
  • 集合:如 HashSet,它是一种基于哈希表的不可变集合。
  • 数组:如 Array 和 Vector,提供高效的随机访问能力。

这些数据结构使得在 Java 中使用函数式编程模式变得更加简单和高效。

1.5. 总结

Vavr 是一个强大的库,通过提供持久和不可变的数据结构,使得函数式编程在 Java 中变得可行。通过使用 Vavr,开发者可以减少副作用,提高代码的可读性和可维护性,从而在日常开发中实现更高的效率和可靠性。

LOGO

Vavr 使我们能够更深入地探索函数式编程的世界,为 Java 开发者提供了更多的工具和思路。

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

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

相关文章

红外探测算法!!!

一、红外探测的基本原理 红外探测基于红外辐射与物体的热状态之间的关系。物体温度越高&#xff0c;辐射能量越大。红外探测器通过接收物体发出的红外辐射&#xff0c;将其转换为电信号&#xff0c;进而实现对目标的探测和识别。 二、红外探测算法的主要类型 背景差分法&…

[自然语言处理]RNN

1 传统RNN模型与LSTM import torch import torch.nn as nntorch.manual_seed(8)def dm01():参数1&#xff1a;输入向量的维数参数2&#xff1a;隐藏层神经元的个数参数3&#xff1a;隐藏层的层数:return:rnn nn.RNN(5, 6, 1)参数1&#xff1a;句子长度sequence_length参数2&am…

九芯电子NVH/NVF语音芯片OTA升级操作方法

OTA&#xff08;Over-The-Air&#xff09;升级是指通过无线网络远程对设备进行软件升级的过程。对于九芯电子NVH/NVF语音芯片&#xff0c;OTA升级可以通过WiFi模组实现&#xff0c;支持MQTT、HTTP等协议&#xff0c;方便快捷‌。 具体操作步骤如下&#xff1a; 1.进入九芯“智…

计算机毕业设计 基于Django的学生选课系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

处理Java内存溢出问题(java.lang.OutOfMemoryError):增加JVM堆内存与调优

处理Java内存溢出问题&#xff08;java.lang.OutOfMemoryError&#xff09;&#xff1a;增加JVM堆内存与调优 在进行压力测试时&#xff0c;遇到java.lang.OutOfMemoryError: Java heap space错误或者nginx报错no live upstreams while connecting to upstream通常意味着应用的…

重头开始嵌入式第四十七天(硬件 ARM裸机开发 RS232 RS4885 IIC)

目录 一.什么是RS232&#xff1f; 1. 历史背景&#xff1a; 2. 电气特性&#xff1a; 3. 连接器类型&#xff1a; 4. 通信特点&#xff1a; 5. 应用场景&#xff1a; 二.什么是RS485&#xff1f; 1. 电气特性&#xff1a; 2. 通信模式&#xff1a; 3. 传输距离与速率&…

技术路线图用什么画?用这个在线工具轻松完成绘制!

在当今快速发展的技术世界中&#xff0c;技术路线图已成为企业和团队不可或缺的战略规划工具。它不仅能够清晰地展示技术发展方向&#xff0c;还能帮助团队成员、利益相关者和投资者更好地理解和参与技术战略的制定过程。但不可否认的是&#xff0c;创建一个有效的技术路线图并…

如何免费为域名申请一个企业邮箱

背景 做SEO的是有老是会有一些网站来做验证你的所有权&#xff0c;这个时候&#xff0c;如果你域名对应的企业邮箱就会很方便。zoho为了引导付费&#xff0c;有很多多余的步骤引导&#xff0c;反倒是让不付费的用户有些迷茫&#xff0c;所以会写这个教程&#xff0c;按照教程走…

虚幻引擎GAS入门学习笔记(二)

虚幻引擎GAS入门(二) 学习位置UE5.3 GAS入门教程重置版 小明 MVC框架与技能初始化 让一开始创建的蓝图的基础GameplayAbility蓝图继承我们写好的BaseGameplayAbility类 创建一个库函数&#xff0c;写一些常用的函数在里面第一个得到玩家与玩家控制器 获取角色面对目标的方向…

c++11~c++20 thread_local

线程局部存储是指对象内存在线程开始后分配&#xff0c;线程结束时回收且每个线程有该对象自己的实例&#xff0c;简单地说&#xff0c;线程局部存储的对象都是独立各个线程的。实际上这并不是一个新鲜个概念&#xff0c;虽然C一直没因在语言层面支持它&#xff0c;但是很早之前…

Coggle数据科学 | 全球AI攻防挑战赛:金融场景凭证篡改检测 baseline

本文来源公众号“Coggle数据科学”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;全球AI攻防挑战赛&#xff1a;金融场景凭证篡改检测 baseline 赛题名称&#xff1a;全球AI攻防挑战赛—赛道二&#xff08;AI核身-金融场景凭证篡改…

集智书童 | FMRFT 融合Mamba和 DETR 用于查询时间序列交叉鱼跟踪 !

本文来源公众号“集智书童”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;FMRFT 融合Mamba和 DETR 用于查询时间序列交叉鱼跟踪 ! 鱼的生长、异常行为和疾病可以通过图像处理方法进行早期检测&#xff0c;这对工厂水产养殖具有重…

基于云效流水线Flow | 高效构建企业门户网站

基于云效流水线Flow | 高效构建企业门户网站 基于云效流水线Flow | 高效构建企业门户网站企业门户网站方案架构一键部署方案概览部署准备一键部署 部署服务端&#xff08;云效流水线&#xff09;添加流水线源Java构建上传主机部署 资源删除操作体验1&#xff09; 在体验过程中是…

Redis 5 种基本数据类型的前两个详解

Redis 共有 5 种基本数据类型&#xff1a;String&#xff08;字符串&#xff09;、List&#xff08;列表&#xff09;、Set&#xff08;集合&#xff09;、Hash&#xff08;散列&#xff09;、Zset&#xff08;有序集合&#xff09;。 这 5 种数据类型是直接提供给用户使用的&…

qos在企业网中的设计与实现

1.拓扑 地址规划 业务地址规划 部门 地址空间 vlan 网关 市场部门 10.0.100.0/24 Vlan100 10.0.100.254/24 研发部门 10.0.101.0/24 Vlan101 10.0.101.254/24 财务部门 10.0.102.0/24 Vlan102 10.0.102.254/24 人力部门 10.0.103.0/24 Vlan103 10.0.103.25…

[nmap] 端口扫描工具的下载及详细安装使用过程(附有下载文件)

nmap网络连接端扫描软件&#xff0c;用于主机发现、端口扫描、版本侦测、操作系统侦测 下载链接在文末 下载压缩包后解压 &#xff01;&#xff01;安装路径不要有中文 解压得到文件 双击.exe文件 更改安装路径并点击安装 等待安装 安装完成 nmap-7.95-setup.zip 夸克网盘打开…

pip install kaggle-environments ISSUE:Failed to build vec-noise

ISSUE: error: Microsoft Visual C 14.0 or greater is required. Get it with “Microsoft C Build Tools”: https://visualstudio.microsoft.com/visual-cpp-build-tools/ [end of output]Failed to build vec-noiseC:\ProgramData\miniconda3\include\pyconfig.h(59): fat…

基于Springboot+Vue的家校互联系统(含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统中…

信息安全工程师(41)VPN概述

前言 VPN&#xff0c;即Virtual Private Network&#xff08;虚拟专用网络&#xff09;的缩写&#xff0c;是一种通过公共网络&#xff08;如互联网&#xff09;创建私密连接的技术。 一、定义与工作原理 定义&#xff1a;VPN是依靠ISP&#xff08;Internet Service Provider&…

国庆档不太热,影视股“凉”了?

今年国庆档票房止步21亿元&#xff0c;属实有点差强人意。 根据国家电影局统计&#xff0c;2024年国庆档&#xff08;2024年10月1日至7日&#xff09;全国电影票房为21.04亿元&#xff0c;观影人次为5209万&#xff0c;总票房成绩、观影总人次同比均有所下滑。 作为传统观影高…