函数式编程要点

一、介绍和入门

1、函数式编程介绍

函数式编程思想在于简化冗余代码,将函数式接口(接口中只有一个未实现的方法)作为最高抽象。它关注的是入参、返回、语法结构,是一种极简的编程方式。相对面向过程和面向对象编程,函数编程是面向数据处理和计算过程的抽象与组合

//在java中可以使用@FunctionalInterface标识函数式接口,其作用是检验接口中是否只有一个未实现的方法,如
@FunctionalInterface
public interface Function<T, R> {R apply(T var1);
}

2、java中的函数式编程接口

在java.util.function包中,提供了大量规范的函数式接口,以避免过多使用自定义函数式接口

主要分为5类

  1. Consumer结尾:接收入参,没有返回
  2. Supplier结尾:没有入参,有返回
  3. Function结尾:接收入参,有返回
  4. Predicate结尾:断言,返回boolean类型参数
  5. Operator结尾:入参和返回参数类型相同
Consumer    接受一个参数,无返回值
BiConsumer    接受两个参数,无返回值
DoubleConsumer    接受一个double类型的参数,无返回值
IntConsumer    接受一个int类型的参数,无返回值
LongConsumer    接受一个long类型的参数,无返回值
ObjDoubleConsumer    接受一个自定义类型的参数和一个double类型的参数,无返回值
ObjIntConsumer    接受一个自定义类型的参数和一个int类型的参数,无返回值
ObjLongConsumer    接受一个自定义类型的参数和一个long类型的参数,无返回值
Function    接受一个参数,有返回值
BiFunction    接受两个参数,有返回值
DoubleFunction    接受一个double类型的参数,有返回值
IntFunction    接受一个int类型的参数,有返回值
LongFunction    接受一个long类型的参数,有返回值
IntToDoubleFunction    接受一个int类型的参数,返回一个double类型的值
IntToLongFunction    接受一个int类型的参数,返回一个long类型的值
LongToDoubleFunction    接受一个long类型的参数,返回一个double类型的值
LongToIntFunction    接受一个long类型的参数,返回一个int类型的值
DoubleToIntFunction    接受一个double类型的参数,返回一个int类型的值
DoubleToLongFunction    接受一个double类型的参数,返回一个long类型的值
ToDoubleBiFunction    接受两个参数,返回一个double类型的值
ToDoubleFunction    接受一个参数,返回一个double类型的值
ToIntBiFunction    接受两个参数,返回一个int类型的值
ToIntFunction    接受一个参数,返回一个int类型的值
ToLongBiFunction    接受两个参数,返回一个long类型的值
ToLongFunction    接受一个参数,返回一个long类型的值
BinaryOperator    接受两个相同类型的参数,返回一个相同类型的值
DoubleBinaryOperator    接受两个double类型的参数,返回一个double类型的值
DoubleUnaryOperator    接受一个double类型的参数,返回一个double类型的值
IntBinaryOperator    接受两个int类型的参数,返回一个int类型的值
IntUnaryOperator    接受一个int类型的参数,返回一个int类型的值
LongBinaryOperator    接受两个long类型的参数,返回一个long类型的值
LongUnaryOperator    接受一个long类型的参数,返回一个long类型的值
UnaryOperator    接受一个参数,返回一个相同类型的值
Predicate    接受一个参数,返回一个boolean类型的值
BiPredicate    接受两个参数,返回一个boolean类型的值
DoublePredicate    接受一个double类型的参数,返回一个boolean类型的值
IntPredicate    接受一个int类型的参数,返回一个boolean类型的值
LongPredicate    接受一个long类型的参数,返回一个boolean类型的值
Supplier    无参数,有返回值
BooleanSupplier    无参数,返回一个boolean类型的值
DoubleSupplier    无参数,返回一个double类型的值
IntSupplier    无参数,返回一个int类型的值
LongSupplier    无参数,返回一个long类型的值

 3、函数式编程配合Lambda表达式的简单实践

    public static void main(String[] args) {//匿名内部类Function<Integer, String> function1 = new Function<Integer, String>() {@Overridepublic String apply(Integer i) {return String.valueOf(i);}};System.out.println(function1.apply(1));//简化Function<Integer, String> function2 = i -> String.valueOf(i);System.out.println(function2.apply(1));//再简化Function<Integer, String> function3 = String::valueOf;System.out.println(function3.apply(1));//多个参数BiFunction<Integer, Integer, Integer> biFunction = (i, j) -> i + j;System.out.println(biFunction.apply(0, 1));//断言Predicate<Integer> predicate = i -> i == 1;//判断是否满足条件System.out.println(predicate.test(2));//判断是否不满足条件System.out.println(predicate.negate().test(2));}

4、函数式编程特点

  1. 不可变性:在函数式编程中,数据是不可变的。这意味着一旦一个值被定义,它就不能被改变。这种不可变性有助于减少程序中的错误和副作用,因为你不必担心在某个地方意外地修改了数据。
  2. 无副作用:函数式编程中的函数通常没有副作用,即它们不会修改全局状态或产生其他外部影响。这使得函数更加可预测和可测试,因为你可以确信给定相同的输入,函数将始终产生相同的输出。
  3. 函数的纯粹性:在函数式编程中,函数是纯粹的,即它们的结果只取决于其输入参数,而不受外部状态的影响。这有助于简化代码和推理过程,因为你不必考虑函数之外的任何状态或变量。
  4. 高阶函数和闭包:函数式编程鼓励使用高阶函数(接受其他函数作为参数或返回函数的函数)和闭包(捕获其外部环境的函数)。这些特性使得函数更加灵活和可组合,可以方便地构建复杂的计算过程和数据结构。
  5. 抽象和组合:函数式编程强调通过抽象和组合来构建复杂的程序。你可以将计算过程分解为一系列简单的函数,然后将它们组合在一起以实现更复杂的功能。这种方式有助于提高代码的可读性、可维护性和可重用性

 5、函数式编程缺点

  1. 学习成本较高:函数式编程的思维方式与传统的命令式编程有很大的不同。它强调不可变性、无副作用和函数的纯粹性,这可能需要开发者花费更多的时间和精力来学习和适应。此外,函数式编程通常使用高阶函数和闭包等高级特性,这也增加了学习的难度。
  2. 性能问题:虽然函数式编程可以提高代码的可读性和可维护性,但在某些情况下,它可能不如命令式编程性能好。这主要是因为函数式编程强调不可变性和无副作用,这可能导致大量的数据复制和函数调用,从而增加了内存消耗和运行时间。然而,现代的函数式编程语言和编译器已经采用了许多优化技术来缓解这个问题。
  3. 调试困难:由于函数式编程强调函数的纯粹性和无副作用,这使得在调试过程中很难跟踪和定位错误。在命令式编程中,你可以通过设置断点、打印日志等方式来跟踪程序的执行过程。但在函数式编程中,由于函数之间没有共享的状态,因此很难确定错误发生的位置和原因。
  4. 与现有系统的兼容性:函数式编程与传统的命令式编程在思维方式和编程风格上有很大的不同。这可能导致在将函数式编程与现有的命令式系统进行集成时出现兼容性问题。例如,函数式编程强调不可变性和无副作用,这可能与现有系统中的某些特性和库不兼容。
  5. 可能的代码可读性问题:虽然函数式编程可以提高代码的简洁性和可维护性,但过度使用某些函数式编程特性(如无参风格、大量方法的组合)可能会影响代码的可读性。这使得其他开发者在阅读和理解代码时可能会遇到困难。

 二、进阶

1、Java8 Stream Api(流操作)

代码示例

    public static void main(String[] args) {  // 创建一个整数列表  List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  // 使用Stream API进行过滤、映射和聚合操作  List<Integer> result = numbers.stream() // 创建流  .filter(n -> n % 2 == 0) // 过滤偶数  .map(n -> n * n) // 映射:将每个数平方  .collect(Collectors.toList()); // 聚合:将结果收集到一个新的列表中  // 输出结果[4, 16, 36, 64, 100]System.out.println(result);  } 

Stream Api特点

  1. 流水线操作:Stream API 的操作可以像流水线一样串联起来,形成一个大的操作链。每个操作都可以看作是对数据源的一个转换或处理步骤。
  2. 内部迭代:Stream API 采用了内部迭代的方式,具体的迭代过程则由流自行完成。这种方式简化了代码。                                               
  3. 不可变性:Stream API 中的流是不可变的。一旦一个流被创建,就不能再修改它的数据源或操作。每次对流进行修改或转换,都会返回一个新的流对象。这种不可变性保证了数据的安全性,避免了在并发环境下的数据竞争问题。
  4. 惰性求值:Stream API 采用了惰性求值的方式。这意味着在中间处理过程中,流只是对操作进行了记录,并不会立即执行。只有当执行到终止操作时,才会进行实际的计算。这种方式可以提高性能,避免不必要的计算。
  5. 丰富的操作:Stream Api简化了Java开发中数据运算、聚合、分组、转换、比较、过滤、遍历、排序等操作,通过使用Lambda表达式和函数式编程,可以简化冗余复杂的代码,这些操作可以方便地满足各种数据处理需求。

Stream Api使用注意点

  1. 流的操作默认多线程并行执行,对于少量数据操作其性能可能不如传统写法,此外在并发编程中流处理的中间操作过程中不应该介入有状态数据,或将流中间操作的结果输出。
  2. 流操作并不是每次对一个中间过程进行处理,而是每次走完整个处理链。

 Stream Api的中间操作和终端操作

  1. 中间操作
    中间操作会返回一个新的流,并允许链式操作。这些操作不会立即执行,而是惰性求值的,即只有当终端操作被触发时,中间操作才会被应用到数据源上。常见的中间操作有:

    • filter(Predicate<? super T> predicate)
      • 过滤流中的元素,只保留满足给定谓词的元素。
    • map(Function<? super T, ? extends R> mapper)
      • 将流中的每个元素映射成一个新的元素,通常用于转换数据类型或提取信息。
    • flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
      • 将流中的每个元素映射成一个新的流,然后将这些流合并成一个流。这通常用于将多个流合并成一个,或将嵌套的数据结构展平。
    • limit(long maxSize)
      • 截断流,使其只包含前 maxSize 个元素。这是一种短路操作,即一旦达到指定的大小,就会停止处理更多的元素。
    • skip(long n)
      • 跳过流中的前 n 个元素。如果流中的元素不足 n 个,则返回一个空流。
    • distinct()
      • 返回一个包含所有不同元素的新流。这是通过流的元素的 equals() 方法进行比较的。
    • sorted() 或 sorted(Comparator<? super T> comparator)
      • 返回一个新流,其中的元素按自然顺序排序,或者根据提供的 Comparator 进行排序。
    • peek(Consumer<? super T> action)
      • 对流中的每个元素执行给定的操作,并返回一个新的流包含原始元素。这主要用于调试目的,因为它允许你查看流中的元素而不改变它们。
    • mapToInt(ToIntFunction<? super T> mapper)mapToLong(ToLongFunction<? super T> mapper)mapToDouble(ToDoubleFunction<? super T> mapper)
      • 这些操作将流中的元素映射成基本数据类型(int, long, double)的流,如 IntStreamLongStreamDoubleStream。这对于需要进行数值计算的情况非常有用。
  2. 终端操作
    终端操作会触发实际的计算,并关闭流。流只能被遍历一次,因此终端操作之后,流就不能再被使用了。常见的终端操作有:

    • forEach(Consumer<? super T> action)
      • 对流中的每个元素执行给定的操作。这通常用于遍历流并执行某种副作用。
    • collect(Collector<? super T, A, R> collector)
      • 使用提供的Collector对流中的元素进行归约操作,并将结果收集到一个目标对象中,通常是集合或其他数据结构。
    • toArray()
      • 将流中的元素收集到一个数组中。这个方法有两种形式:无参的toArray(),它返回一个Object[],以及toArray(IntFunction<A[]> generator),它允许你指定数组的类型和大小。
    • reduce(T identity, BinaryOperator<T> accumulator)
      • 使用给定的累积函数对流中的元素进行归约操作,并返回归约的结果。identity是归约的初始值。
    • min(Comparator<? super T> comparator) 和 max(Comparator<? super T> comparator)
      • 根据提供的比较器返回流中的最小或最大元素。返回的是一个Optional<T>,表示最小或最大元素可能存在也可能不存在。
    • count()
      • 返回流中的元素数量。这是一个长整型值,可以处理比Integer.MAX_VALUE更大的流。
    • anyMatch(Predicate<? super T> predicate)
      • 判断流中是否有任何元素匹配给定的谓词。如果有一个元素满足条件,就返回true
    • allMatch(Predicate<? super T> predicate)
      • 判断流中的所有元素是否都匹配给定的谓词。只有当所有元素都满足条件时,才返回true
    • noneMatch(Predicate<? super T> predicate)
      • 判断流中是否没有任何元素匹配给定的谓词。如果所有元素都不满足条件,就返回true
    • findFirst()
      • 返回流中的第一个元素。返回的是一个Optional<T>,表示第一个元素可能存在也可能不存在。
    • findAny()
      • 返回流中的任意一个元素。在并行流中,这个方法可能会更高效,因为它允许从任何部分获取元素。返回的也是一个Optional<T>

这两种操作类型一起构成了 Stream API 强大的数据处理和分析能力。中间操作允许我们构建复杂的查询和转换管道,而终端操作则负责触发实际的计算和结果提取。

本文引用

函数式编程有哪些好处?_函数式编程语言有哪些_Js函数式编程好处 - 腾讯云开发者社区 - 腾讯云

Stream流API总结_stream流常用api-CSDN博客

 java Stream类的 超全API及实战总结_java stream api-CSDN博客

在Stream流中添加中间操作_stream添加元素-CSDN博客

在Stream上添加终端操作_tounmodifiableset-CSDN博客

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

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

相关文章

社区养老|社区养老服务系统|基于springboot社区养老服务系统设计与实现(源码+数据库+文档)

社区养老服务系统目录 目录 基于springboot社区养老服务系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员部分功能 &#xff08;1&#xff09; 用户管理 &#xff08;2&#xff09;服务种类管理 &#xff08;3&#xff09;社区服务管理 &#xff08…

【教学类-19-08】20240214《ABAB式-规律黏贴18格-手工纸15*15CM-一页3种图案,A空,纵向、无边框》(中班)

背景需求 利用15*15CM手工纸制作AB色块手环&#xff08;手工纸自带色彩&#xff09;&#xff0c;一页3个图案&#xff0c;2条为一组&#xff0c;黏贴成一个手环 素材准备 代码展示 # # 作者&#xff1a;阿夏 # 时间&#xff1a;2024年2月14日 # 名称&#xff1a;正方形数字卡…

Linux文件信息,drwxr-xr-x. 2 root root 6 Jan 30 17:42 Desktop

drwxr-xr-x. 2 root root 6 Jan 30 17:42 Desktop drwxr-xr-x. drwxr-xr-x.d是文件类型rwx r-x r-x9位&#xff0c;每3位一组&#xff0c;一共3组&#xff0c;代表基本权限第一组 文件的创建者 | 拥有者第二组 和拥有者在一个组中第三组 其他用户rread&#xff0c;读的权限ww…

2023 车载摄像头产业链梳理

1. 车载摄像头的发展和市场空间 车载摄像头&#xff0c;最早在车载行车记录仪、倒车影像功能中被应用。而随着汽车产业步入智能化&#xff0c; 360 环 视 、 ADAS 、 智 能 座 舱 等 应 用 为 车 载 摄 像 头 带 来 了 巨 大 的 市 场 需 求 。 特 别 是 在 自 动 驾驶 、 36…

BBC英式口语~发音练习~笔记整理

参考资料 原视频地址&#xff1a; https://www.bilibili.com/video/BV1D7411n7bS/?spm_id_from333.1245.0.0&vd_source5986fc7c8e6d754f3ca44233573aeaff 笔记图片

MySQL篇之覆盖索引

一、定义 覆盖索引是指查询使用了索引&#xff0c;并且需要返回的列&#xff0c;在该索引中已经全部能够找到。 二、例子 1. id为主键&#xff0c;默认是主键索引。 2. name字段为普通索引。 select * from tb_user where id 1 覆盖索引 select id&#xff0c;na…

如何选择适合的社区店项目,开启创业之路

对于想要创业的人来说&#xff0c;选择一个适合的社区店项目是成功的关键。在这篇文章中&#xff0c;我将以一名资深鲜奶吧创业者的身份&#xff0c;分享一些关于如何选择适合的社区店项目的经验和见解&#xff0c;希望能给大家提供有价值的参考。 一、市场调研 在选择社区店…

数据结构实验之栈与队列五:下一较大值(一)

数据结构实验之栈与队列五&#xff1a;下一较大值&#xff08;一&#xff09; Description 对于包含n&#xff08;1<n<1000&#xff09;个整数的序列&#xff0c;对于序列中的每一元素&#xff0c;在序列中查找其位置之后第一个大于它的值&#xff0c;如果找到&#xff…

海量数据处理商用短链接生成器平台 - 4

第六章 架构核心技术-池化思想-异步结合 性能优化最佳实践 第1集 RestTemplate里面的存在的问题你知道多少- Broken pipe错误 项目就更新到第六章了&#xff0c;剩下的内容 放百度网盘里面了&#xff0c;需要的来取。 链接&#xff1a;https://pan.baidu.com/s/19LHPw36dsxPB7…

四、JMS规范

JMS规范 一、JMS是什么二、MQ中间件对比三、JMS组成1.JMS Provider2.JMS Producer3.JMS Consumer4.JSM Message4.1 消息头4.2 消息体4.2.1 生产者4.2.2 消费者 4.3 消息属性 四、JMS可靠性1.PERSISTENT - 持久化1.1 参数设置1.2 Queue持久化1.3 Topic持久化1.3.1 持久的发布主题…

Linux文件操作类命令 touch | stat | cp | scp | alias | mv | dd | cat | head | tail

touch 1.创建空文件&#xff08;只有元数据信息&#xff0c;没有数据段内容&#xff09;touch 1.txt 2.对创建或已经存在的文件改变元数据信息的时间记录值-d 改变创建时间touch -d 20240101 1.txt -a改变访问时间 -m改变修改时间stat 查看元数据信息 Blocks是512字…

KMP算法失配处理

已知字符串s为“abaabaabacacaabaabcc”&#xff0c;模式串T为“abaabc”。采用KMP算法进行匹配&#xff0c;第一次出现“失配”(s[i]≠t[j])时&#xff0c;ij5&#xff0c;则下次开始匹配时i和j的值分别是( ) A.i1&#xff0c;j0 B.i5&#xff0c;j0 C.i5&#xff0c…

排序算法---桶排序

原创不易&#xff0c;转载请注明出处。欢迎点赞收藏~ 桶排序&#xff08;Bucket Sort&#xff09;是一种排序算法&#xff0c;它将待排序的数据分到几个有序的桶中&#xff0c;每个桶再分别进行排序&#xff0c;最后将各个桶中的数据按照顺序依次取出&#xff0c;即可得到有序序…

【机器学习笔记】 9 集成学习

集成学习方法概述 Bagging 从训练集中进行子抽样组成每个基模型所需要的子训练集&#xff0c;对所有基模型预测的结果进行综合产生最终的预测结果&#xff1a; 假设一个班级每个人的成绩都不太好&#xff0c;每个人单独做的考卷分数都不高&#xff0c;但每个人都把自己会做的…

在 Android 上部署自定义 YOLOv8 教程

在本教程中&#xff0c;我将向您展示如何在 Android 设备上使用自定义数据集部署 YOLOv8。想要了解如何在 Android 设备上使用您自己的数据集部署 YOLOv8&#xff1f;本文将展示如何操作。 Android 上的 自定义 YOLOv8 &#x1f525; ⚡️ 结果显示标题 对从 GoPro 流式传输到移…

Sora时代,我们的AI应该何去何从?——关于Sora大模型的思考

Sora时代&#xff0c;我们的AI应该何去何从?——关于Sora大模型的思考 一、Sora大模型&#xff1a;横空出世&#xff0c;让AI生成所有领域瑟瑟发抖二、Sora的出现代表了相关行业的灭亡&#xff1f;三、我们将何去何从&#xff1f; 一、Sora大模型&#xff1a;横空出世&#xf…

【机器学习案例7】计算机视觉中的小物体检测:基于补丁的方法

专栏导读 作者简介:工学博士,高级工程师,专注于工业软件算法研究本文已收录于专栏:《机器学习实用指南》本专栏旨在提供 1.机器学习经典案例及源码;2.开源机器学习训练数据集;3.机器学习前沿专业博文。以案例的形式从实用的角度出发,快速上手机器学习项目,在案例中成长…

掌握XGBoost:GPU 加速与性能优化

导言 XGBoost是一种强大的机器学习算法&#xff0c;但在处理大规模数据时&#xff0c;传统的CPU计算可能会变得缓慢。为了提高性能&#xff0c;XGBoost可以利用GPU进行加速。本教程将介绍如何在Python中使用XGBoost进行GPU加速以及性能优化的方法&#xff0c;并提供相应的代码…

1.8 NLP自然语言处理

NLP自然语言处理 更多内容&#xff0c;请关注&#xff1a; github&#xff1a;https://github.com/gotonote/Autopilot-Notes.git 一、简介 seq2seq(Sequence to Sequence)是一种输入不定长序列&#xff0c;产生不定长序列的模型&#xff0c;典型的处理任务是机器翻译&#…

[C# WPF] 如何给控件添加边框(Border)?

在WPF中&#xff0c;可以使用边框控件或者边框属性来为控件添加边框。 以下是两种常见的方法&#xff1a; 方法1&#xff1a;使用边框控件&#xff08;Border&#xff09; WPF中的Border控件用于为其他控件添加边框效果。它是一个容器控件&#xff0c;可以包含一个子元素&…