java中函数是什么_[一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念...

本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍

什么是函数式编程?

java程序员第一反应可能会理解成类的成员方法一类的东西

此处并不是这个含义,更接近是数学上的函数

看一下百度百科中关于函数的说明

函数的定义:

给定一个数集A,假设其中的元素为x。

现对A中的元素x施加对应法则f,记作f(x),得到另一数集B。假设B中的元素为y。

则y与x之间的等量关系可以用y=f(x)表示。

我们把这个关系式就叫函数关系式,简称函数。

函数概念含有三个要素:定义域A、值域C和对应法则f。

其中核心是对应法则f,它是函数关系的本质特征。

1bfb0d417f1512c92a18208ea9adb054.png

对应于编程来说,当然不是完全的数学上的函数定义

所谓函数式编程我们可以理解为:

通过对应法则f(x) 对指定的x 进行处理,映射成另外一个值

而且不会对x本身产生变动

所谓不会对x产生变动,你可以理解为无副作用,或者说副作用不会被察觉

副作用你可以理解为解题过程中对数据的修改

说起来好像很啰嗦,但是如果有人告诉你 通过sin(x) 计算后, x的值被改变了,你不会觉得异常奇怪么

函数式编程就是把函数的一些特性应用于编程语言之中

注意:

函数式编程不是某一种语言,也不是某个API

他是一种方法论,是一种编程范式,有它自有的一些特性和规定

语言中引入函数式编程,也就是用语言本身定义了函数式编程的一些特性和规定

函数式编程最重要的基础是λ演算,而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。

它一套用于研究函数定义、函数应用和递归的形式系统

我们只需要知道λ演算是一种形式的匿名函数,并且接收一个参数作为输入 (可以柯里化进行参数转换多参数函数转换为单参数)

有兴趣的可以去探究下λ演算

函数式编程有下列特性

闭包和高阶函数

闭包就是能够读取其他函数内部变量的函数,是个不太好理解的概念

此处我们仅仅理解成 函数可以当做值进行传递并且可以使用变量保存 是"第一等公民"

一等公民或者一等类型的含义就是指可以跟值一样的地位,作为参数传递或者存储于变量中

高阶函数是指可以用另一个函数(间接地,用一个表达式) 作为其输入参数,比如 f(g(x))=g(x)+1 的形式

惰性计算

表达式不是在绑定到变量时立即计算,而是在求值程序需要产生表达式的值时进行计算

你可以理解为流水线上每一个节点都只是做了一系列的设置,并没有立刻去计算数值

没有副作用

副作用是指在运算过程中,修改了函数内部局部变量以外的其他变量的状态,比如你修改了类成员变量

没有副作用也就意味着不产生运算以外的其他结果,不修改系统的变量

引用透明性

如果提供同样的输入,那么函数总是返回同样的结果

也就是说表达式的值不依赖于可以改变值的全局状态,比如不依赖成员变量的值

为什么要使用函数式编程?

关注做什么 更接近于自然语言

任务分解后你的解题思路将是如何调用各个不同的函数,要做什么

不在关注于函数内部的细节本身去思考怎么做

假设有这么一组Student学生类型的List数据,学生有性别男女

如果在Java代码中,你会如何解题?

伪代码:

List男List ;for(int i=0;i

if(studentList[i].性别 ==男){

男List.add(List[i])

}

你循环遍历列表,找到符合条件的学生,然后把他加入另外一个列表,这可能是一种常见的解题思路

假设有个Student 学生表,每条记录都有一个性别字段值为男女

如果是在数据库中查询呢,一种可能的解法是这样子的

select

*

fromstudentwheresex='男';

他们的主要区别是什么?

一个最直观的差别就是:

java代码中是你自己去循环数据项,你自己处理每一项数据,找出符合你要求的数据

SQL查询中,你只是传入通知条件where  sex='男';  ,数据库在自己内部进行了循环,帮我们找出来符合要求的数据

这就是外部循环和内部循环,这是一种思维方式的转变

外部循环,需要程序员自己去关注每一个数据项

内部循环,程序员只需要关注结果

内部循环以及函数调用 也将我们从如何做中解放出来,让我们不再关注数据项循环的细节本身,仅仅关注于此次调用的结果

不管是什么方式进行思考编程,你都会将你的任务进行分解

划分为更小的子任务

但是不同的是:

在如何做的思维下,你还需要思考在每个子任务中,每一个细节是怎么处理的,比如循环中进行条件判断

这其实还是往计算机的思维倾斜的一种思考方式,这是指令式或者命令式的编程模式

在做什么的思维下,你不在关注每个子任务的内部细节,只在乎结果也就是"做什么"

每个子任务内部的细节是函数自己内部的事情,这更加符合人的思维习惯

内部循环不也是函数式编程的一种表现形式么

函数本身如同一个黑盒一般,有输入有输出,我们不关心内部的实现细节,仅仅在乎输入和输出

内部循环也是如此,我们告诉他我们想要的结果行为,他返回给我们结果

比如SQL中

where   sex='男';  这就是对我们行为的描述(不要把它理解成筛选条件)

我们将行为像参数一样传递给了数据库软件,数据库执行查询操作,根据的是我们给定的行为

这就是行为参数化的魅力所在

行为参数化也是一种思维模式,只要能把行为像参数一样进行传递  就是行为参数化

有人可能已经想到了匿名内部类

new Thread(newRunnable() {

@Overridepublic voidrun() {

System.out.println();

}

}).start();

的确这也是一种行为参数化,但是显然,这段代码还不够简洁纯粹,因为方法的外层还套了一层对象

Java8中的行为参数化,传递的将是更加纯粹的行为,而不再需要借助一个匿名对象的形式,而且,Lambda表达式不会像内部类一样生成一个类

传递的是方法本身,方法中的代码本身

那么行为参数化,不也就是函数式编程中的闭包特性么

更加易于并发编程

函数式编程的准则是没有副作用不依赖外部的数据,也不改变外部数据的值。

我们知道线程安全的根本在于共享数据,如果没有任何的数据共享,那么很多的并发/线程安全问题都将迎刃而解

所以说这一特性正好满足了多核并行程序设计的需求,所以很显然能够简化并行程序的开发

函数式编程代码简洁

函数式编程大量使用函数,减少了代码的重复,就如同你调用别人的方法一样不是么,一行就得到了结果

Java8 对于函数式编程的支持

编程语言把函数式编程的概念引入,也就是使自身支持函数式编程的特性,换句话说也就是

在语言内部可以使用一系列的类型或者关键字或者符号组合等进行表示

Java主要涉及这三个核心概念

函数接口(FunctionalInterface)

流(Stream)

收集器(Collector)

函数接口

既然函数式编程要求函数可以是同值一样的一等公民用于参数化传递,那么必须要有表示函数的类型

先说一下函数式接口的注解

注解@FunctionalInterface描述了什么是一个函数式接口

public @interface FunctionalInterface {}

05858fd505cde90b2f2f582e083b34f6.png

上面的注释也就相当于是函数式接口的定义:

一个函数式接口只能有一个抽象方法,default方法有实现,所以不是抽象方法

如果一个接口声明了一个覆盖Object  public公有方法的抽象方法,也不算是抽象方法

所以说:函数式接口,有且仅有一个抽象方法,覆盖Object的public方法不计算在内(如果是覆盖Object的protected那么会计数的)

比如

java.lang.Runnable、java.util.Comparator是典型的函数式接口

函数接口是一个接口,有且只有一个唯一的抽象方法

接口上定义了函数的类型参数

抽象方法的方法签名限定了函数(函数式接口的抽象方法的签名称为函数描述符)

所以说一个函数接口,只能描述一种类型的函数

比如

Function      这个函数接口

4af3c40698ed6cb37e3838a2a2438907.png

他表示形如

R function(T){

....

return R

}

他的类型参数是T  R,调用方法apply 输入为T   输出为R

作用为转换一个对象为不同类型的对象

所有这种形式的函数都是这个函数接口类型

比如

public static voidmain(String[] args){

Function function = (String x)->x.equals("true");

System.out.println(function.apply("1"));

System.out.println(function.apply("true"));

}

b13b948e8b8863ca5f1c1d14202df40f.png

至此,Java中已经有了用于表示函数的类型了,也就是可以定义一个函数或者返回一个函数,或者把函数当做一个参数值进行传递了

以赋值运算符的形式来类比的话就是

比如

int i = 1;

等号左边的类型已经有了就是函数接口

但是右边,也就是行为参数化这个行为到底如何表示呢?也即是上面的1 的位置

在java中可以使用

Lambda表达式((String x)->x.equals("true"))

方法引用(String::length)

两种形式进行表示

比如

public static voidmain(String[] args){

Function function =String::length;

System.out.println(function.apply("1"));

System.out.println(function.apply("true"));

}

b49760939baf679d0b3bafcb76d80901.png

既然每一种函数类型都需要存在指定形式的函数接口,想要使用Lambda-匿名函数或者方法引用,自然需要定义函数接口

函数类型的说法可能不太准确,函数式接口的抽象方法的签名称为函数描述符 其实说的也都还是方法签名  方法签名唯一的标识了一个函数

Java8 也已经给我们预置了一些常用的函数接口类型

已经定义一套能够描述常见函数描述符的函数接口

比如上面提到的

function  就是其中一种

另外还有其他一些,后面再说,我们已经可以在Java中表示一个函数,并且对函数进行调用

流,流动,流水,java中早就已经有了IO流,形象的表达了数据在程序中的处理与流动

Java8中的Stream流则更倾向于流水线的含义

每个节点有各自独立的功能目的,根据你的目的(做什么),将各个独立的功能目的节点拼接成一整个的完整的流水线

数据在此流水线上进行加工处理,最终得出结果

通过告知Stream "做什么" 来进行数据操作和处理

你不在需要关注内部的细节,Stream通过内部迭代进行数据项的筛选查找,找到符合条件的数据

流(Stream)是Java8对函数式编程的重要支撑。大部分函数式工具都围绕Stream展开

也可以说Stream类是Java8 关于函数式编程定义的一些列函数集合

由此可以看得出来,Stream的重要性

想要使用Java进行函数式编程,仅仅使用Lambda表达式是不够的,必须有足够的函数,Lambda表达式只有跟stream一起使用才能显示其真实的威力

集合是一种数据结构用于存储数据

Stream不是一种数据结构,是对于数据的一种新的视图,用于数据的计算,提供了一系列的API用于调用

概括的说

Stream就是函数式编程中编程语言提供出来的库方法集合,而参数基本上都是函数

所以才说,Lambda表达式只有跟stream一起使用才能显示其真实的威力

f0d6ae11ec5997f9f837af33c4ebae06.png

常用的Stream调用流程

e63c74698f386c666064f28684101205.png

1.获得Stream

想要使用Stream的一些特性,显然你必须把你的数据集转换生成为Stream,这没有Stream何谈使用?

2.设置行为类型 也就是操作类型

这句话有些模糊不清,其实就是你需要设置想干什么

到底是筛选数据?转换数据?求和还是怎样?

你可以类比为SQL查询中到底是SELECT 还是UPDATE 或者DELETE? 这就是行为的类型

为了更快理解的话,你可以片面的理解为调用Stream类的方法

我们举例说明

比如你经常让同学帮你买东西

买东西就是行为类型,是去买东西,既不是帮你开车也不是陪你看电影

这就是行为的类型

Stream中有一系列的API可以帮助我们达到这个目的

比如 filter  map等等

3. 确定行为参数 也就是操作内容

行为参数也就是基于已经设置的行为类型下,你具体要以什么样子的行为去执行

你筛选数据筛选什么样子的数据?

转换数据,转换为什么形式?

类比为SQL查询中就是查询条件,查询  男生?查询 女生? 这就是行为的具体方式

还是刚才的例子,你经常让同学帮你买东西,那到底买什么?买矿泉水还是买面包?这就是确定行为参数

Java8中使用方法引用或者Lambda-匿名函数  或者方法引用来表示行为参数

4.行为的属性

既然是流水线式的工作方式,那么当前的工作结束后或许结束了或许是进入到流水线的下一环节

当然最终他肯定还是会结束掉的

这就又涉及到绑定行为方法的属性种类  到底是中间的操作(可以继续传递给流水线下一步)  还是结束的终端操作

中间操作的返回结果还是一个Stream  你仍旧可以对他进行上述类似的过程

终端操作则一般会将流进行收集整理成指定的数据结构

这基本上是一个常用的Stream使用流程

流程处理虽然很简单,但是强大之处在于中间操作处理后仍旧是流

这就意味着你可以按照需要进行无数的变换组合以达到你想要的效果

收集器

Stream结合Lambda表达式可以对于数据进行各种各样的操作

但是Stream 终归是Stream ,它并不是一种数据结构,不管经过了多少处理,他终归是再次返回到代码中具体的其他数据类型中

把Stream类比做数据项处理的流水线的话

中间操作就是流水线上的一个个的功能操作节点

而收集器就是在某些结束操作中用于将数据进行转换的工具

在Java中关于收集器有几个关键的概念

1. Stream中的collect 方法是收集器的调用者

R collect(Collector super T, A, R> collector);

2. Collector 接口 定义了收集器

public interface Collector {

3. 收集器工厂Collectors  用于预置一些收集器

public final class Collectors

比如   .....collect(Collectors.toList());  就是把一个处理后的流转换为List

总结:

Java8 构建了三个主要概念,函数接口,流,收集器

有了函数接口  函数拥有了类型也就是可以像值一样作为参数进行传递,作为返回值,或者使用变量进行表示

使用Lambda-匿名函数或者方法引用来表示行为参数  也就是函数的值

Stream是Java8 提供的函数式编程的"库函数" 预定了一些常用的操作模式,通过Lambda表达式结合使用

收集器用于把Stream处理后的数据进行打包整理成你需要的数据结构

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

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

相关文章

分布式系统不得不说的CAP定理

21天学会C语言?3天学会弹钢琴?放弃一切错误方法,从今天开始“刻意练习”,因为这才是最强大的,也是唯一正确的学习方法。--《刻意练习》Anders Ericsson引言CAP问题已经成了计算机科学中一个研究领域,之前说…

[PAT乙级]1021 个位数统计

输入格式: 每个输入包含 1 个测试用例,即一个不超过 1000 位的正整数 N。 输出格式: 对 N 中每一种不同的个位数字,以 D:M 的格式在一行中输出该位数字 D 及其在 N 中出现的次数 M。要求按 D 的升序输出。 输入样例:…

Largest Rectangle in a Histogram (动态规划+奇思妙想单调栈)求最大矩状图面积

感觉动态规划都是玄妙的很,思维题吧(单调栈思维) 题解:让求最大矩形面积,宽为1,暴力超时 可以发现 当第i-1个比第i个高的时候 比第i-1个高的所有也一定比第i个高 于是可以用到动态规划的思想 令l…

ASP.NET Core分布式项目实战(详解oauth2授权码流程)--学习笔记

最近公司产品上线,通宵加班了一个月,一直没有更新,今天开始恢复,每日一更,冲冲冲任务13:详解oauth2授权码流程我们即将开发的产品有一个用户 API,一个项目服务 API,每个服务都需要认…

C++类模板中的static成员

从类模板实例化的每一个模板类有自己的类模板数据成员&#xff0c;该模板的所有对象共享一个static数据成员。 代码如下: #include <iostream> using namespace std;template<typename T> class Person { public:static int a;};template<typename T> int …

C#并发编程之初识并行编程

写在前面之前微信公众号里有一位叫sara的朋友建议我写一下Parallel的相关内容&#xff0c;因为手中商城的重构工作量较大&#xff0c;一时之间无法抽出时间。近日&#xff0c;这套系统已有阶段性成果&#xff0c;所以准备写一下Parallel的相关内容&#xff0c;正好也延续之前的…

java 下拉列表 枚举_「Java三分钟」精准而优雅——枚举类详解

关注我&#xff0c;每天三分钟&#xff0c;带你轻松掌握一个Java相关知识点。1.为什么要用枚举你在读一个老工程代码时&#xff0c;是否经常看见有几个类&#xff0c;里面放着成百上千的静态常量&#xff0c;场面相当恐怖&#xff0c;而且如果不加注释&#xff0c;很多你都不知…

Not so Mobile(二叉树递归输入同时建树){天平}

题意&#xff1a; 给出一个大天平&#xff0c;大天平中还有许多小天平&#xff0c;求出所有的天平是否平衡&#xff1b;平衡条件为wldl wrdr&#xff1b; 题目 Before being an ubiquous communications gadget, a mobile was just a structure made of strings and wires …

在阿里云函数计算上部署.NET Core 3.1

使用阿里云ECS或者其他常见的VPS服务部署应用的时候&#xff0c;需要手动配置环境&#xff0c;并且监测ECS的行为&#xff0c;做补丁之类的&#xff0c;搞得有点复杂。好在很多云厂商&#xff08;阿里云、Azure等&#xff09;提供了Serverless服务&#xff0c;借助于Serverless…

java 持续交付_【Java架构:持续交付】一篇文章搞掂:Jenkins

1.1、使用yum安装JDKa、检查系统是否有安装open-jdkrpm -qa |grep javarpm -qa |grep jdkrpm -qa |grep gcj如果没有输入信息表示没有安装。如果安装可以使用rpm -qa | grep java | xargs rpm -e --nodeps 批量卸载所有带有Java的文件 这句命令的关键字是javab、检索yum中包含…

抱歉,请不要把 “业务逻辑层” 理解为 “业务中台”

这是头哥侃码的第197篇原创在IAS2019中台架构峰会上&#xff0c;我曾与一位年轻帅气的技术小伙来了一番有趣的对话。因为和朋友有约&#xff0c;所以我在现场互动结束之后&#xff0c;就急匆匆地跟其他嘉宾打了声招呼&#xff0c;抱着笔记本冲出了会场。但没想到刚到电梯口&…

C++异常处理分析

C异常处理基本语法: 代码如下: #include <iostream> using namespace std;int divide(int x, int y) {if (y 0) throw y;return x / y; }void test01() {//试着去捕获异常try{divide(10, 0);}/*catch (int){cout << "除数为0!" << endl;} */catc…

陌陌的 Service Mesh 探索与实践

Service Mesh Virtual Meetup 是 ServiceMesher 社区和 CNCF 联合主办的线上系列直播。本期为 Service Mesh Virtual Meetup#1 &#xff0c;邀请了四位来自不同公司的嘉宾&#xff0c;从不同角度展开了 Service Mesh 的应用实践分享&#xff0c;分享涵盖来自陌陌和百度的 Servi…

C标准输入流

标准输入流对象cin&#xff0c;重点掌握的函数: cin.get()//一次只能读取一个字符 cin.get(一次参数)//读一个字符 cin.get(两个字符)//可以读字符串 cin.getline() cin.ignore() cin.peek() cin.putback() 标准输入流cin.get() 代码如下: #include <iostream> using n…

Harmonic Number(欧拉公式或技巧打表)LightOJ - 1234(求调和级数的和)

题意&#xff1a;求f(n)1/11/21/31/4…1/n (1 ≤ n ≤ 108).&#xff0c;精确到10-8 (原题在文末&#xff09; 知识点&#xff1a;调和级数(即f(n))至今没有一个完全正确的公式&#xff0c;但欧拉给出过一个近似公式&#xff1a;(n很大时) f(n)≈ln(n)C1/2*n 欧拉常数值&…

教你配置windows上的windbg,linux上的lldb,打入clr内部这一篇就够了

一&#xff1a;背景1. 讲故事前几天公众号里有位兄弟看了几篇文章之后&#xff0c;也准备用windbg试试看&#xff0c;结果这一配就花了好几天&#xff0c;(づ╥﹏╥)づ&#xff0c;我想也有很多跃跃欲试的朋友在配置的时候肯定会遇到这样和那样的问题&#xff0c;所以我觉得有必…

C标准输出流

标准输入流对象cin&#xff0c;重点掌握的函数: cout.flush()//刷新缓冲区 cout.put()//向缓冲区写字符 cout.write()//二进制流的输出 cout.width()//输出格式控制 cout.fill() cout.set(标记) cout.flush() 代码如下: #include <iostream> using namespace std;void…

Autofac在.NET Core 中的使用

前言Autofac 是一款.NET IoC 容器 . 它管理类之间的依赖关系, 从而使应用在规模及复杂性增长的情况下依然可以轻易地修改 。.NET CORE 中也内置了依赖注入&#xff0c;但是有些情况下需要用到Autofac去进行依赖注入&#xff0c;Autofac支持的所有注入方式以外&#xff0c;还支持…

详解.NET Core 依赖注入生命周期

前言.NET Core 自带依赖注入框架&#xff0c;支持三种不同生命周期的注入模式&#xff1a;Singleton 单例模式Scoped 区域模式Transient 瞬时模式但是常常不知道什么时候使用哪种模式才最合适&#xff0c;接下来我就用代码详细解读一下三种模式代码示例首先新建.NET Core API…

[C++STL]string容器用法介绍

string构造函数 代码如下: #include <iostream> #include <string> using namespace std;void test01() {string s1;cout << "s1 " << s1 << endl;const char *str "hello world";string s2(str);cout << "s2…