关于lambda:基于亚马逊云科技无服务器服务快速搭建电商平台性能篇

应用 Serverless 构建独立站的劣势在传统架构模式下,如果须要进行电商大促须要提前预置计算资源以撑持高并发拜访,会造成计算资源节约并且减少运维工作量。本文介绍一种新的部署形式,将 WordPress 和 WooCommerce 部署在 Amazon Lambda 中。Lambda 是无服务器的计算形式,无需预置资源就能够运行代码,主动响应任何规模的代码执行申请,从每天十几个事件到每秒数十万个事件,按计算工夫付费(以毫秒为单位),真正做到按使用量计费,从而达到节俭预置资源和运维老本。Lambda 的这种个性,让 Lambda 越来越受欢迎,越来越多的客户抉择 Lambda 来部署利用,其中也蕴含 web 利用。理解 Lambda 的客户可能分明,Lambda 是基于事件触发的形式,对于 web 利用,须要应用 API Gateway,接管 HTTP 申请,把 HTTP 申请转化为 Lambda 事件触发 Lambda 运行。 亚马逊云科技开发者社区为开发者们提供寰球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、流动与比赛等。帮忙中国开发者对接世界最前沿技术,观点,和我的项目,并将中国优良开发者或技术举荐给寰球云社区。如果你还没有关注/珍藏,看到这里请肯定不要匆匆划过,点这里让它成为你的技术宝库!在以前,对于已有的 Web 利用,需对利用代码进行轻量级的革新以解决 Lambda 事件。对于很多应用像 WordPress 和 WooCommerce 这样的成熟组件的电商客户来讲,进行代码革新不太可能,是不是就不能利用 Lambda 的劣势了呢?答案是否定的。利用 Lambda 的新性能 Lambda container images 和开源组件 Amazon Lambda adapter 能够让 WordPress 在 Lambda 中运行且无需进行任何代码的批改。本解决方案通过将 Lambda Adapter,WordPress,WooCommerce 以及其余必要插件打包成容器,部署到 Lambda。同时本解决方案也利用了 Lambda 的新性能,Function URL,来代替 API Gateway,能够间接通过Function URL 来通过 HTTP(s) 拜访 Lambda,从而节俭 API Gateway 带来的老本。用户的动静申请,通过 CloudFront 回源到 Lambda URL 触发 Lambda 运行,在 Lambda 外部,Lambda Adapter 接管到 Lambda 事件并将其转换成 WordPress 能解决的 HTTP 申请。这样就实现了无需批改代码就能在 Lambda 中运行 WordPress。 ...

August 27, 2023 · 2 min · jiezi

关于lambda:这样也行在lambda表达式中优雅的处理checked-exception

简介最近发现很多小伙伴还不晓得如何在lambda表达式中优雅的解决checked exception,所以明天就重点和大家来探讨一下这个问题。 lambda表达式自身是为了不便程序员书写不便的工具,应用lambda表达式能够让咱们的代码更加简洁。 可能大多数小伙伴在应用的过程中素来没有遇到过外面蕴含异样的状况,所以对这种在lambda表达式中异样的解决可能没什么教训。 不过没关系,明天咱们就来一起探讨一下。 lambda表达式中的checked exceptionjava中异样的类型,大家应该是耳熟能详了,具体而言能够有两类,一种是checked exception, 一种是unchecked exception。 所谓checked exception就是须要在代码中手动捕捉的异样。unchecked exception就是不须要手动捕捉的异样,比方运行时异样。 首先咱们定义一个checked exception,间接继承Exception就好了: public class MyCheckedException extends Exception{ @java.io.Serial private static final long serialVersionUID = -1574710658998033284L; public MyCheckedException() { super(); } public MyCheckedException(String s) { super(s); }}接下来咱们定义一个类,这个类中有两个办法,一个抛出checked exception,一个抛出unchecked exception: public class MyStudents { public int changeAgeWithCheckedException() throws MyCheckedException { throw new MyCheckedException(); } public int changeAgeWithUnCheckedException(){ throw new RuntimeException(); }}好了,咱们首先在lambda表达式中抛出CheckedException: public static void streamWithCheckedException(){ Stream.of(new MyStudents()).map(s->s.changeAgeWithCheckedException()).toList(); }这样写在现代化的IDE中是编译不过的,它会提醒你须要显示catch住CheckedException,所以咱们须要把下面的代码改成上面这种: ...

April 12, 2023 · 2 min · jiezi

关于lambda:无服务器的又一个里程碑看亚马逊云科技-Serverless-的创新演进

“2022 年 12 月,在拉斯维加斯举办的 2022 亚马逊云科技 re:Invent 寰球大会完满闭幕,5 场主题论坛和数百场涵盖领导力、技术、行业、合作伙伴的分论坛以及丰盛的技术利用展现,行业前沿技术,又清晰了诸多行业将来发展趋向,一览云端有限美景。有哪些惊艳的新技术和实际?咱们一起开始摸索!”作为云计算的下一个迭代, Serverless 无服务器让开发者能够更关注于构建产品中的利用,而不须要治理和保护底层堆栈,且比传统云计算更为便宜,因而无服务器被誉为“开发新利用最疾速的形式,同时也是总成本最低的形式”。Serverless 让工程师能以现代化的办法构建软件,只须要关注业务逻辑,更快地把产品推向市场。这也为企业缩小了后期投资,能够专一于推动差异化的业务价值,放慢投产工夫,这些也影响着企业的运作形式,从而可能产生多米诺骨牌效应,在市场竞争中一直获利。 一骑绝尘,Serverless 始创者其实早在 2006 年,亚马逊云科技公布了其第一个存储服务 Amazon Simple Storage Service (Amazon S3) 就是无服务器架构的,早在“概念”还没有产生时,亚马逊云科技曾经实现了服务的搭建,相似的事件不足为奇,比方 Amazon DynamoDB 在 2012 年公布,在性能上曾经具备了起初呈现的“云原生数据库”概念的所有定义。 2014 年,亚马逊云科技公布了业界第一个 Serverless 计算服务 Amazon Lambda 。在过来几年的 re:lnvent 大会上,亚马逊云科技一直在数据库、数据分析、人工智能畛域扩大 Serverless 幅员,亚马逊云科技的 16 年发展史( 2006 到 2022 年),也是一部亚马逊云科技发明和深耕 Serverless 的历史, Serverless 演进的历史。 作为无服务器技术的先驱,Amazon Lambda 在采用率方面始终放弃领先地位,有一半的函数即服务(FaaS)用户在应用亚马逊云科技的服务。公开数据显示,已有上百万家客户在用 Amazon Lambda 来构建他们的服务。 往年 re:lnvent 2022 上,更进一步的公布了 Amazon Lambda SnapStart 实现高达 90%的冷启动延时,让用户简直能够无感知的实现利用的扩大。Serverless 的冷启动速度失去大幅优化,大数据外围产品全面 Serverless 化实现,这宣告 Serverless 技术倒退的又一里程碑到来,云产品全面 Serverless 化只剩下工夫问题。 ...

December 30, 2022 · 1 min · jiezi

关于lambda:云途加油站-一文读懂-Dynatrace-与Amazon-Lambda的双剑合璧心法

本文由Dynatrace撰写。理解更多Dynatrace 为亚马逊云科技提供的业余工具,欢送关注: 【插入公众号】 Amazon Lambda 正在掀起企业级云市场的一场小潮流。不少业内人士发现,越来越多的企业正在将 Lambda 函数退出其技术栈中。 这一潮流其实不难理解—— 一则,门槛低。Amazon Lambda为企业进入云计算提供了绝对较低的门槛,无需立刻全面推行转移降级操作。 二则,经济实用。Amazon Lambda 函数严格按应用状况计费,这意味着在 Lambda 函数未运行时产生的老本为零。 三则,功能丰富。Amazon Lambda 函数简直能够承当运行利用的所有操作职责。企业人员须要做的就是提供应该在给定事件或触发器上执行的代码。这意味着不须要配置或治理服务器(甚至容器),Lambda 就曾经解决以下所有工作: 负载平衡主动伸缩失败重试平安隔离操作系统治理利用率和容量因而,一个设计良好的 Lambda 架构能够为企业节俭大量老本。不过,凡事无利必有弊,尽管在运行性能时简直没有经营开销,但在迁徙到 Amazon Lambda 时却有三大挑战须要关注。 挑战1:本地开发困难重重Lambda 函数在 Amazon Lambda 运行时环境中运行并深度集成到 Amazon 技术栈中。因而,在本地开发期间提供雷同的环境可能会很简单。 此外,“Serverless”意味着还须要 Lambda 函数以外的产品技术。应用 Lambda 时,您可能很快就会用到更多其余的Serverless产品,例如数据库。这使得在本地模仿雷同的环境变得困难重重。 挑战2:部署编排难度加大尽管 Amazon 能够加重企业运行函数的经营开销,但更大规模地应用 Lambda 函数则须要成熟的部署策略。那么有没有两败俱伤之法? 实际上, 基础设施即代码 (IaC)兴许正能解您的当务之急。IaC 在这里的最大效用,即是防止最终陷入手工部署性能实例的陷阱。至于开发阶段,咱们则应该关注继续集成和继续部署。思考到这一事实,Dynatrace Lambda 监测扩大反对所有支流的 IaC 技术。 Lambda 部署办法 挑战3:企业级可观测能力难以实现在开发人员开始采纳 Amazon Lambda 后不久,运维和网站可靠性工程团队便会开始千方百计打造等同于传统工作负载提供的洞察力水准。很快,只专一于监测 Lambda 部署的专用产品呈现了。不过,它们却无奈满足企业对外部部署与混合云之间的、端到端的可观测能力常见需要。这是因为,尽管这些专用产品大多建设在 Amazon CloudWatch 和 Xray 之上,但它们的代理有很大的局限性,因而在过程中运行代理和 Lambda 函数会带来一些难题。 而随着 Dynatrace Lambda 扩大的公布,这种场面已成为过来。现在,Dynatrace 能够监测混合环境中的 Lambda,因此可能满足企业的需要。 ...

August 24, 2022 · 1 min · jiezi

关于lambda:常用的-Lambda-表达式案例解析工作中都会用到

咱们日常工作中,Lambda 应用比拟多的场景,就是汇合类下的 Lambda 流操作,往往几行代码能够帮忙咱们实现简单代码 接下来咱们把 Lambda 流的罕用办法用案列解说一下。 ForEach汇合的遍历 forEach 办法 Collect将操作后的对象转化为新的对象 FilterFilter 为过滤的意思,只有满足 Filter 表达式的数据就能够留下来,不满足的数据被过滤掉 Mapmap 办法能够让咱们进行一些流的转化,比方原来流中的元素是 A,通过 map 操作,能够使返回的流中的元素是 B MapToIntmapToInt 办法的性能和 map 办法一样,只不过 mapToInt 返回的后果曾经没有泛型,曾经明确是 int 类型的流了,源码如下: Distinctdistinct 办法有去重的性能 SortedSorted 办法提供了排序的性能,并且容许咱们自定义排序 groupingBygroupingBy 是可能依据字段进行分组,toMap 是把 List 的数据格式转化成 Map 的格局 FindFirstfindFirst 示意匹配到第一个满足条件的值就返回 Reducereduce 办法容许咱们在循环外面叠加计算值 Peekpeek 办法很简略,咱们在 peek 办法外面做任意没有返回值的事件,比方打印日志 Limitlimit 办法会限度输入值个数,入参是限度的个数大小 Max,Min通过 max、min 办法,能够获取汇合中最大、最小的对象 总结本文咱们介绍十几种 Lambda 表达式罕用的办法 懂这些,这样你在工作中遇到简单数据结构转化时,必定会得心应手了。 ...

May 30, 2022 · 1 min · jiezi

关于lambda:使用-Amazon-DevOps-Guru-for-Serverless-自动检测-Lambda-函数中的运行问题

前言咱们于 2022年5月 发表推出 Amazon DevOps Guru for Serverless ,这是面向 Amazon DevOps Guru https://aws.amazon.com/devops...的全新性能。通过此性能,开发人员可能进步无服务器应用程序的运行性能和可用性。该产品链接可点击:https://aws.amazon.com/devops... 亚马逊云科技于2014年推出Amazon Lambda https://aws.amazon.com/blogs/...,创始了无服务器计算畛域的先河。现在,成千上万的客户正在应用 Amazon Lambda,Amaon Lambda 容许您为函数配置许多参数,例如内存调配、预置并发和超时。对于许多客户来说,在所有这些参数之间找到适当的均衡以优化其函数的性能和可用性是一项艰巨的工作。该产品链接可点击https://aws.amazon.com/cn/lam... 2020 年 12 月,咱们发表推出 Amazon DevOps Guru,这是一项齐全托管式 AIOps(面向 IT 经营的人工智能)服务,可自动检测应用程序问题并向客户收回警报,帮忙他们进步应用程序的可用性 https://aws.amazon.com/es/blo...。明天,咱们发表推出 Amazon DevOps Guru for Serverless,这是 Amazon DevOps Guru 的一项新性能,可帮忙应用 Amazon Lambda 的开发人员在函数级别自动检测异样行为,并应用基于机器学习的倡议来修复检测到的任何问题。 Amazon DevOps Guru for Serverless 应用机器学习来自动识别和剖析Amazon Lambda 函数的各种性能和可用性相干问题,例如低预置并发或内存利用有余。要应用此性能,您无需成为无服务器或 ML 专家。 此性能的被动洞察可帮忙您无效地解决影响无服务器应用程序的继续问题,并提供可行的倡议,帮忙您在尽可能短的工夫内找出根本原因并解决问题。 Amazon DevOps Guru for Serverless 还提供被动洞察,帮忙您在无服务器应用程序性能受到影响之前就发现更宽泛的运行异常情况。还为您提供无关如何查找根本原因并解决问题的倡议。 检测到问题时,Amazon DevOps Guru for Serverless 会在 Amazon DevOps Guru 控制台中显示发现后果,同时应用 Amazon EventBridge https://aws.amazon.com/eventb...或 Amazon Simple Notification Service(Amazon SNS)https://aws.amazon.com/sns/ 发送告诉。这可让开发人员主动治理发现的问题并即时采取行动。 ...

May 25, 2022 · 2 min · jiezi

关于lambda:Lambda表达式应用于Java

Java 8的一个大亮点是引入Lambda表达式,应用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。上面这个例子就是应用Lambda语法来代替匿名的外部类,代码不仅简洁,而且还可读。Lambda 容许把函数作为一个办法的参数(函数作为参数传递进办法中)。应用 Lambda 表达式能够使代码变的更加简洁紧凑。lambda 表达式的语法格局如下:(parameters) -> expression或(parameters) ->{ statements; } 没有应用Lambda的老办法: button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent actionEvent){ System.out.println("Action detected"); }});应用Lambda: button.addActionListener( actionEvent -> { System.out.println("Action detected");});让咱们来看一个更显著的例子。不采纳Lambda的老办法: Runnable runnable1=new Runnable(){@Overridepublic void run(){ System.out.println("Running without Lambda"); }};应用Lambda: Runnable runnable2=()->System.out.println("Running from Lambda");正如所看到的,应用Lambda表达式不仅让代码变的简略,并且可读,最重要的是代码量也随之缩小很多。然而,在某种程度上,这些性能在Scala等这些JVM语言里曾经被宽泛应用。并不奇怪,Scala社区是难以置信的,因为许多Java 8里的内容看起来就像是从Scala里搬过去的。在某种程度上,Java 8的语法要比Scala的更具体但不是很清晰,但这并不能阐明什么,如果能够,它可能会像Scala那样构建Lambda表达式。一方面,如果Java持续围绕Lambda来倒退和实现Scala都曾经实现的性能,那么可能就不须要Scala了。另一方面,如果它只提供一些外围的性能,例如帮忙匿名外部类,那么Scala和其余语言将会持续茁壮成长,并且有可能会凌驾于Java之上。其实这才是最好的后果,有竞争才有提高,其它语言持续倒退和成长,并且无需放心是否会过期。

December 7, 2021 · 1 min · jiezi

关于lambda:Lambda表达式

Lambda表达式1.什么是Lambda?Lambda是Java8增加的新个性,说白了,Lambda是一个一名函数2.Lambda根底语法Lambda是一个匿名函数 个别关注一下两个重点1.参数列表2.办法体():用来形容参数列表{}:用来形容办法体->:Lambda运算符 读作goes to/** * 无参数无返回值接口 */@FunctionalInterfacepublic interface LambdaNoneReturnNoneParmeter { void test();}/** * 无返回值有单个参数 */@FunctionalInterfacepublic interface LambdaNoneReturnSingleParmeter { void test(int n);}/** * 无返回值 多个参数的接口 */@FunctionalInterfacepublic interface LambdaNoneReturnMutipleParmeter { void test(int a,int b);}/** * 有返回值 无参数接口 */@FunctionalInterfacepublic interface LambdaSingleReturnNoneParmeter { int test();}/** * 有返回值 有单个参数的接口 */@FunctionalInterfacepublic interface LambdaSingleReturnSingleParmeter { int test(int n);}/** * 有返回值 有多个参数的接口 */@FunctionalInterfacepublic interface LambdaSingleReturnMutipleParmeter { int test(int a,int b);}/** * @author Alan */public class Syntax1 { public static void main(String[] args) { // 1.Lambda表达式的根底语法 // Lambda是一个匿名函数 个别关注的是以下两个重点 // 参数列表 办法体 /** * ():用来形容参数列表 * {}:用来形容办法体 * ->: Lambda运算符 读作goes to */ // 无参无返回 LambdaNoneReturnNoneParmeter lambda1=()->{ System.out.println("hello word"); }; lambda1.test(); // 无返回值 单个参数 LambdaNoneReturnSingleParmeter lambda2=(int n)->{ System.out.println("参数是:"+n); }; lambda2.test(10); // 无返回值 多个参数 LambdaNoneReturnMutipleParmeter lambda3=(int a,int b)->{ System.out.println("参数和是:"+(a+b)); }; lambda3.test(10,12); // 有返回值 无参数 LambdaSingleReturnNoneParmeter lambda4=()->{ System.out.println("lambda4:"); return 100; }; int ret=lambda4.test(); System.out.println("返回值是:"+ret); // 有返回值 单个参数 LambdaSingleReturnSingleParmeter lambda5=(int a)->{ return a*2; }; int ret2= lambda5.test(3); System.out.println("单个参数,lambda5返回值是:"+ret2); //有返回值 多个参数 LambdaSingleReturnMutipleParmeter lambda6=(int a,int b)->{ return a+b; }; int ret3=lambda6.test(12,14); System.out.println("多个参数,lambda6返回值是:"+ret3); }}输入后果: hello word 参数是:10 参数和是:22 lambda4: 返回值是:100 单个参数,lambda5返回值是:6 多个参数,lambda6返回值是:263.语法精简3.1.参数类型精简/*** 语法精简* 1.参数类型* 因为在接口的形象办法中,曾经定义了参数的数量类型 所以在Lambda表达式中参数的类型能够省略* 备注:如果须要省略类型,则每一个参数的类型都要省略,千万不要一个省略一个不省略*/LambdaNoneReturnMutipleParmeter lambda1=(int a,int b)-> { System.out.println("hello world"); }; 能够精简为:LambdaNoneReturnMutipleParmeter lambda1=(a,b)-> { System.out.println("hello world");};3.2.参数小括号精简/*** 2.参数小括号* 如果参数列表中,参数的数量只有一个 此时小括号能够省略*/LambdaNoneReturnSingleParmeter lambda2=(a)->{ System.out.println("hello world");};能够精简为:LambdaNoneReturnSingleParmeter lambda2= a->{ System.out.println("hello world");};3.3.办法大括号精简/*** 3.办法大括号* 如果办法体中只有一条语句,此时大括号能够省略*/LambdaNoneReturnSingleParmeter lambda3=a->{ System.out.println("hello world");};能够精简为:LambdaNoneReturnSingleParmeter lambda3=a->System.out.println("hello world");3.4.大括号精简补充/*** 4.如果办法体中惟一的一条语句是一个返回语句* 省略大括号的同时 也必须省略return*/LambdaSingleReturnNoneParmeter lambda4=()->{ return 10;};能够精简为:LambdaSingleReturnNoneParmeter lambda4=()->10;3.5.多参数,有返回值 精简LambdaSingleReturnNoneParmeter lambda4=(a,b)->{ return a+b;};能够精简为:LambdaSingleReturnMutipleParmeter lambda5=(a,b)->a+b;4.Lambda语法进阶public class Syntax3 { public static void main(String[] args) { LambdaSingleReturnSingleParmeter lambda1=a->a*2; LambdaSingleReturnSingleParmeter lambda2=a->a*2; LambdaSingleReturnSingleParmeter lambda3=a->a*2; //简化 LambdaSingleReturnSingleParmeter lambda4=a->change(a); //办法援用 LambdaSingleReturnSingleParmeter lambda5=Syntax3::change; } /** * 自定义的实现办法 */ private static int change(int a){ return a*2; }}

July 26, 2021 · 2 min · jiezi

关于lambda:Lamda拉姆达表达式演化过程

简介HI!小伙伴们,好久没见了,4月份开始断更,中途有点事儿,明天开始更新了,整顿一篇Lamda表达式演化过程,心愿喜爱的判若两人的反对! 传统汇合过滤当初有2个需要:在一组学生汇合中,1.找出年龄大于20学生;2.找出分数小于70的学生;上面让咱们看看代码吧! /** * @author :jiaolian * @date :Created in 2021-07-12 15:14 * @description:学生测试 * @modified By: * 公众号:叫练 */public class Student { //姓名 private String name; //年龄 private int age; //分数 private double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; }}** ...

July 16, 2021 · 2 min · jiezi

关于lambda:lambda-生成笛卡尔集

public static void getDiKaEr() { List<List<String>> source = new ArrayList<>(); List<String> color = Arrays.asList("红色", "红色", "蓝色"); List<String> size = Arrays.asList("中", "大", "小"); List<String> origins = Arrays.asList("中国", "瑞士"); source.add(color); source.add(size); source.add(origins); List<String> result = source.stream().reduce((a, b) -> { List<String> results = new ArrayList<>(); a.forEach(param1 -> { b.forEach(param2 -> { results.add(param1 + "-" + param2); }); }); return results; }).get(); System.out.println(result);}

April 11, 2021 · 1 min · jiezi

关于lambda:Lambda架构概览工作原理优缺点和适用场景

现在,物联网(IoT)、社交媒体、应用程序、以及剖析设施,都在继续产生着各种类型的海量数据。而咱们的业务零碎须要每天接管各种大数据,并实现各项解决工作。因而,在日常解决这些持续增长的数据时,数据系统会面临提早和准确性两个方面的挑战。 Lambda架构的介绍 针对上述挑战,Nathan Marz和James Warren于2015年首发了Lambda体系架构。它在逻辑上将数据系统分为三个层面,即:批处理(batch)层、速度(speed)层和服务(serving)层。而作为一种大数据的范例,它能够让用户通过构建数据系统,以克服上述数据提早与准确性等问题。 因为Lambda体系架构能够被程度扩大,因而如果您的数据集过大,或所需的数据视图过多,则能够通过增加更多的主机来参加解决。不过,Lambda会将零碎中最简单的局部,限度在速度层中。而因为该层面的输入是长期的,因而如果您须要对数据进行改良或校对,则能够每隔几小时清空一次。 Lambda体系架构的工作原理 Lambda体系架构的第一层--批处理层 ,既能够存储整个数据集,又可能计算出批处理的视图。因为此处的存储数据集不可被扭转,因而只能被追加。也就是说,新的数据会一直地被传入,而原有旧的数据则会始终保持不变。同时,批处理层会通过对整个数据集的查问,或功能性计算,得出各种视图。查问这些视图时,咱们尽管能够在整个数据集中低提早地找到答案,然而其毛病是零碎须要破费大量的工夫,来进行计算。Lambda体系架构的第二层--服务层,可能批量加载视图。与传统数据库类似,它通过对视图的只读查问操作,来提供低提早的响应。一旦批处理层筹备好一组新的视图,服务层就会将以后已过期的批处理视图予以替换。流到批处理层的数据,同样也会流入Lambda体系架构的第三层--速度层。其次要区别在于,只管批处理层从开始就保留了所有数据,然而速度层仅关怀从最初一批视图实现以来达到的数据。也就是说,速度层通过解决那些批处理视图尚未计入的最新数据查问,来补救计算视图时的高提早。具体原理如下图所示: 比如阐明三个层面 为了更好地了解上述概念,让咱们来打个比方:在一位老人的豪宅里,每个房间都有一个时钟。然而,除了厨房里的时钟外,其余时钟都是不准的。他须要以厨房里的时钟为基准去校准其余时钟。不过,因为记忆力差,他必须将厨房时钟上的以后工夫(上午9:04)记在一张纸上。而后,他以迟缓的步调走向各个房间,将所有时钟设定为上午9:04。而当他最初达到东厢房时,理论工夫曾经是上午9:51了。显然,他后续在各个房间时钟上设置的上午9:04,都是谬误。 同理,如果数据系统只有批处理层,那么咱们就会遇到相似的问题—因为须要破费一段时间能力失去某个问题的答案,因而该答案对于继续涌入的数据并非最新。 让咱们回到方才的例子,幸好这位老人手上有一只秒表。次日上午9:04,他同样从厨房开始,在一张纸上记下工夫,并启动秒表(也就是他的“速度层”)。当最初达到东厢房时,他的秒表上显示为“47分16秒”。通过根本数学计算,他能够晓得以后的时钟应该被设置为9:51 AM。 在上述类比中,老人是服务层,其豪宅里各个房间的时钟随处能够显示以后工夫的批处理视图。当然,他通过触发秒表,让批处理视图会与速度层同步,以取得最精确的答案。 为什么要应用Lambda体系架构? 在Marz和Warren无关Lambda架构的开创性著述--《大数据》中,他们列出了大数据系统中的八个现实属性,也形容了Lambda架构如何去满足每一种属性: 鲁棒性和容错能力。因为批处理层被设计为追加式,即蕴含了自开始以来的整体数据集,因而该零碎具备肯定的容错能力。如果任何数据被损坏,该架构则能够删除从损坏点以来的所有数据,并替换为正确的数据。同时,批处理视图也能够被换成齐全被从新计算出的视图。而且速度层能够被抛弃。此外,在生成一组新的批处理视图的同时,该架构能够重置整个零碎,使之从新运行。可扩展性。Lambda体系架构的设计层是作为分布式系统被构建的。因而,通过简略地增加更多的主机,最终用户能够轻松地对系统进行程度扩大。通用性。因为Lambda体系架构是个别范式,因而用户并不会被锁定在计算批处理视图的某个特定形式中。而且批处理视图和速度层的计算,能够被设计为满足某个数据系统的特定需要。延展性。随着新的数据类型被导入,数据系统也会产生新的视图。数据系统不会被锁定在某类、或肯定数量的批处理视图中。新的视图会在实现编码之后,被增加到零碎中,其对应的资源也会失去轻松地延展。按需查问。如有必要,批处理层能够在短少批处理视图时,反对长期查问。如果用户能够承受长期查问的高提早,那么批处理层的用处就不仅限于生成的批处理视图了。起码的保护。Lambda架构的典型模式是:批处理层应用Apache Hadoop,而服务层应用ElephantDB。显然,两者都很容易被保护。可调试性。Lambda体系架构通过每一层的输出和输入,极大地简化了计算和查问的调试。低提早的读取和更新。在Lambda体系架构中,速度层为大数据系统提供了对于最新数据集的实时查问。Lambda体系架构的毛病 事物往往都有两面性,Lambda架构除了具备上述长处,也存在着如下毛病: 因为所有数据都是被追加进来,并且批处理层中的任何数据都不会被抛弃,因而零碎的扩大老本必然会随着工夫的推移而增长。如前文所述,批处理层可应用Hadoop或Snowflake,而速度层则能够应用Storm或Spark。显然,这两层尽管运行同一组数据,然而它们是在齐全不同的零碎上构建的。因而,用户须要保护两套互相独立的零碎代码。这样岂但简单,而且极具肯定的挑战性。机器学习中的Lambda架构 在机器学习畛域,数据量无疑是多多益善的。然而,对于机器学习利用算法、以及检测模式而言,它们须要以一种有意义的形式,去接收数据。因而,机器学习能够受害于由Lambda架构构建的数据系统,所解决的各类数据。据此,机器学习算法能够提出各种问题,并逐步对输出到零碎中的数据进行模式识别。 物联网的Lambda架构 如果说机器学习利用的是Lambda架构的输入,那么物联网则更多地应用到了数据系统的输出。构想一下,一个领有数百万辆汽车的城市,每辆汽车都装有传感器,并可能发送无关天气、空气质量、交通状况、地位信息、以及司机驾驶习惯等数据。这些海量数据流,会被实时馈入Lambda体系架构的批处理层和速度层,进行后续解决。能够说,物联网设施是正当应用大数据源的绝佳示例。 流解决和Lambda架构挑战 速度层也被称为“流解决层”。其指标是提供最新数据的低提早实时视图。虽说,速度层仅关怀,自实现最初一组批处理视图以来导入的数据,但事实上它不会存储这些小局部的数据。这些数据在流入时就会被立刻解决,且在实现后被立刻抛弃。因而,咱们能够认为这些数据是尚未被批处理视图所计入的数据。 Lambda体系架构在其原始实践中,提到了“最终精度(eventual accuracy)”的概念。它是指:批处理层更关注准确计算,而速度层则关注近似计算。此类近似计算最终将由下一组视图所取代,以便零碎向“最终精度”迈进。 在理论利用中,由实时处理流以毫秒为单位,继续产生的用于更新视图的数据流,是一个非常复杂的过程。在此,我建议您将基于文档的数据库、索引、以及查问零碎配合在一起应用。 Lambda架构和Kappa架构之间的差别 如上所述,因为Lambda体系架构的批处理层和速度层分属不同的分布式系统,咱们须要为类似的解决形式,保护两个独自的代码库。而Kappa架构则通过齐全删除批处理层,来解决该问题。 具体而言,Kappa应用单个流解决层,既通过最新的数据计算来产生实时视图,又对所有数据进行计算,以产生批处理视图。就整个数据集而言,它以追加日志的模式放弃原有数据不变,并且保证数据可能疾速地流过零碎,以产生具备准确计算的视图。同时,来自Lambda架构的原始“速度层”工作,也会被保留在Kappa 架构中,并继续为低提早的视图提供近似计算。据此,这种为单个系统生成视图的形式,大幅简化了零碎的代码库。 通过Heroku上的容器实现Lambda体系架构 通过应用Docker,咱们能够轻松地在启动和试验阶段,实现对Lambda架构所需的各种工具的协调和部署。例如,咱们能够应用基于容器的云平台即服务(PaaS)--Heroku,来部署和扩大应用程序。对于批处理层,您能够应用Apache Hadoop来部署一个Docker容器;针对速度层,您能够思考部署Apache Storm或Apache Spark;而对于服务层,您能够为Apache Cassandra或MongoDB部署Docker容器,并通过Elasticsearch来进行索引和查问。 论断 综上所述,Lambda架构之类的范例具备肯定的扩展性和鲁棒性。随着大量数据流一直地被导入数据系统,批处理层提供了高提早的精度,而速度层提供了低提早近似值。同时,速度层通过协调两种视图,来为查问提供最佳的响应。当然,应用Lambda架构来施行数据系统并非易事,咱们往往须要借助适当的工具,来实现部署与构建。

March 4, 2021 · 1 min · jiezi

关于lambda:Lambda表达式详解

Lambda表达式详解Lambda简介#Lambda 表达式是 JDK8 的一个新个性,能够取代大部分的匿名外部类,写出更优雅的 Java 代码,尤其在汇合的遍历和其余汇合操作中,能够极大地优化代码构造。 JDK 也提供了大量的内置函数式接口供咱们应用,使得 Lambda 表达式的使用更加不便、高效。 对接口的要求#尽管应用 Lambda 表达式能够对某些接口进行简略的实现,但并不是所有的接口都能够应用 Lambda 表达式来实现。Lambda 规定接口中只能有一个须要被实现的办法,不是规定接口中只能有一个办法 jdk 8 中有另一个新个性:default, 被 default 润饰的办法会有默认实现,不是必须被实现的办法,所以不影响 Lambda 表达式的应用。@FunctionalInterface#润饰函数式接口的,要求接口中的形象办法只有一个。 这个注解往往会和 lambda 表达式一起呈现。 Lambda 根底语法#咱们这里给出六个接口,后文的全副操作都利用这六个接口来进行论述。 /**多参数无返回*/@FunctionalInterfacepublic interface NoReturnMultiParam { void method(int a, int b);}/**无参无返回值*/@FunctionalInterfacepublic interface NoReturnNoParam { void method();}/**一个参数无返回*/@FunctionalInterfacepublic interface NoReturnOneParam { void method(int a);}/**多个参数有返回值*/@FunctionalInterfacepublic interface ReturnMultiParam { int method(int a, int b);}/*** 无参有返回*/@FunctionalInterfacepublic interface ReturnNoParam { int method();}/**一个参数有返回值*/@FunctionalInterfacepublic interface ReturnOneParam { int method(int a);}语法模式为 () -> {},其中 () 用来形容参数列表,{} 用来形容办法体,-> 为 lambda运算符 ,读作(goes to)。 ...

February 25, 2021 · 3 min · jiezi

关于lambda:Lambda1-List相关Lambda表达式使用上篇

Lambda在jdk1.8外面曾经很好用了,在这里不讲底层的实现,只有简略的用法,会持续补全。首先一个list咱们要应用lambda的话,须要应用它的stream()办法,获取流,能力应用后续的办法。 根底类User.javapublic class User { public long userId; public User() { } public User(long userId, String name, int age) { this.userId = userId; this.name = name; this.age = age; } public String name; public int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public long getUserId() { return userId; } public void setUserId(long userId) { this.userId = userId; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", userId=" + userId + '}'; } public void output() { System.out.println("User{" + "name='" + name + '\'' + ", age=" + age + ", userId=" + userId + '}'); }}1.遍历元素应用foreach办法,其中s->外面的s指list外面的每一个元素,针对每一个元素都执行后续的办法。如果外面只有一句话,能够间接缩写foreach(n -> System.out.println(n));,如果须要执行的办法外面有两句或者多句须要执行的话,须要能够应用list.stream().forEach(s -> {System.out.println(s);});模式。 ...

December 26, 2020 · 3 min · jiezi

关于lambda:map和peek区别

1.Stream<T> peek(Consumer<? super T> action); peek办法接管一个Consumer的入参。理解表达式的应该明确 Consumer的实现类 应该只有一个办法,该办法返回类型为void Consumer<Integer> c = i -> System.out.println("hello" + i);2.<R> Stream<R> map(Function<? super T, ? extends R> mapper); Function 的 表达式 能够这样写 Function<Integer,String> f = x -> {return "hello" + i;};咱们发现Function 比 Consumer 多了一个 return。 这也就是peek 与 map的区别了。 总结:peek接管一个没有返回值的表达式,能够做一些输入,内部解决等。map接管一个有返回值的表达式,之后Stream的泛型类型将转换为map参数表达式返回的类型

December 19, 2020 · 1 min · jiezi

关于lambda:精通lambda表达式Java多核编程pdf

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 什么是Lambda表达式? Lambda表达式是JDK8的一个新个性,能够取代大部分的匿名外部类,写出更优雅的Java代码,尤其在汇合的遍历和其余汇合操作中,能够极大地优化代码构造。 JDK也提供了大量的内置函数式接口供咱们应用,使得Lambda表达式的使用更加不便、高效。 传统的CPU,只有一个内核,这个内核也只可能同时运行一个线程。采纳超线程技术的CPU,能够在一颗内核上同时运行多个线程。而多核CPU更是在一个CPU上嵌入多颗采纳超线程技术的内核。这样,多核CPU就能够同时运行更多的线程。 Java则从一开始就在语言层面上反对多线程。应用Java编写多线程的程序是非常简单的。因而,基本上所有的Java程序,包含J2ME、J2SE、J2EE程序都应用了多线程技术。 最近很多小伙伴问我要一些 lambda表达式 相干的材料,于是我翻箱倒柜,找到了这本十分经典的电子书——《精通lambda表达式:Java多核编程》。 材料介绍 《精通lambda表达式:Java多核编程》介绍JavaSE8中与lambda相干的个性是如何帮忙Java迎接下一代并行硬件架构的挑战的。本书解说了如何编写lambda、如何在流与汇合解决中应用lambda,并且提供了残缺的代码示例。非常适合开发者的学习。 如何获取? 1.辨认二维码并关注公众号「Java后端技术全栈」; 2.在公众号后盾回复关键字「964」

November 17, 2020 · 1 min · jiezi

关于lambda:Lambda架构已死去ETL化的IOTA才是未来

通过这么多年的倒退,曾经从大数据1.0的BI/Datawarehouse时代,通过大数据2.0的Web/APP过渡,进入到了IOT的大数据3.0时代,而随之而来的是数据架构的变动。 ▌Lambda架构 在过来Lambda数据架构成为每一个公司大数据平台必备的架构,它解决了一个公司大数据批量离线解决和实时数据处理的需要。一个典型的Lambda架构如下: 数据从底层的数据源开始,通过各种各样的格局进入大数据平台,在大数据平台中通过Kafka、Flume等数据组件进行收集,而后分成两条线进行计算。一条线是进入流式计算平台(例如 Storm、Flink或者Spark Streaming),去计算实时的一些指标;另一条线进入批量数据处理离线计算平台(例如Mapreduce、Hive,Spark SQL),去计算T+1的相干业务指标,这些指标须要隔日能力看见。 Lambda架构经验多年的倒退,其长处是稳固,对于实时计算局部的计算成本可控,批量解决能够用早晨的工夫来整体批量计算,这样把实时计算和离线计算顶峰离开,这种架构撑持了数据行业的晚期倒退,然而它也有一些致命毛病,并在大数据3.0时代越来越不适应数据分析业务的需要。毛病如下: ● 实时与批量计算结果不统一引起的数据口径问题:因为批量和实时计算走的是两个计算框架和计算程序,算出的后果往往不同,常常看到一个数字当天看是一个数据,第二天看昨天的数据反而产生了变动。 ● 批量计算在计算窗口内无奈实现:在IOT时代,数据量级越来越大,常常发现夜间只有4、5个小时的工夫窗口,曾经无奈实现白天20多个小时累计的数据,保障早上下班前准时出数据已成为每个大数据团队头疼的问题。 ●数据源变动都要从新开发,开发周期长:每次数据源的格局变动,业务的逻辑变动都须要针对ETL和Streaming做开发批改,整体开发周期很长,业务反馈不够迅速。 ● 服务器存储大:数据仓库的典型设计,会产生大量的两头后果表,造成数据急速收缩,加大服务器存储压力。 ▌Kappa架构** 针对Lambda架构的须要保护两套程序等以上毛病,LinkedIn的Jay Kreps结合实际教训和集体领会提出了Kappa架构。Kappa架构的核心思想是通过改良流计算零碎来解决数据全量解决的问题,使得实时计算和批处理过程应用同一套代码。此外Kappa架构认为只有在有必要的时候才会对历史数据进行反复计算,而如果须要反复计算时,Kappa架构下能够启动很多个实例进行反复计算。 一个典型的Kappa架构如下图所示: Kappa架构的核心思想,包含以下三点: 1.用Kafka或者相似MQ队列零碎收集各种各样的数据,你须要几天的数据量就保留几天。 2.当须要全量从新计算时,从新起一个流计算实例,从头开始读取数据进行解决,并输入到一个新的后果存储中。 3.当新的实例做完后,进行老的流计算实例,并把老的一些后果删除。 Kappa架构的长处在于将实时和离线代码对立起来,不便保护而且对立了数据口径的问题。而Kappa的毛病也很显著: ● 流式解决对于历史数据的高吞吐量力不从心:所有的数据都通过流式计算,即使通过加大并发实例数亦很难适应IOT时代对数据查问响应的即时性要求。 ● 开发周期长:此外Kappa架构下因为采集的数据格式的不对立,每次都须要开发不同的Streaming程序,导致开发周期长。 ● 服务器老本节约:Kappa架构的外围原理依赖于内部高性能存储redis,hbase服务。然而这2种零碎组件,又并非设计来满足全量数据存储设计,对服务器老本重大节约。 ▌IOTA架构** 而在IOT大潮下,智能手机、PC、智能硬件设施的计算能力越来越强,而业务需要要求数据实时响应需要能力也越来越强,过来传统的中心化、非实时化数据处理的思路曾经不适应当初的大数据分析需要,我提出新一代的大数据IOTA架构来解决上述问题,整体思路是设定规范数据模型,通过边缘计算技术把所有的计算过程扩散在数据产生、计算和查问过程当中,以对立的数据模型贯通始终,从而进步整体的估算效率,同时满足即时计算的须要,能够应用各种Ad-hoc Query来查问底层数据: IOTA整体技术构造分为几局部: *● Common Data Model*:贯通整体业务始终的数据模型,这个模型是整个业务的外围,要放弃SDK、cache、历史数据、查问引擎保持一致。对于用户数据分析来讲能够定义为“主-谓-宾”或者“对象-事件”这样的形象模型来满足各种各样的查问。以大家相熟的APP用户模型为例,用“主-谓-宾”模型形容就是“X用户 – 事件1 – A页面(2018/4/11 20:00) ”。当然,依据业务需要的不同,也能够应用“产品-事件”、“地点-工夫”模型等等。模型自身也能够依据协定(例如 protobuf)来实现SDK端定义,地方存储的形式。此处外围是,从SDK到存储到解决是对立的一个Common Data Model。 ● Edge SDKs & Edge Servers:这是数据的采集端,不仅仅是过来的简略的SDK,在简单的计算状况下,会赋予SDK更简单的计算,在设施端就转化为造成对立的数据模型来进行传送。例如对于智能Wi-Fi采集的数据,从AC端就变为“X用户的MAC 地址-呈现- A楼层(2018/4/11 18:00)”这种主-谓-宾构造,对于摄像头会通过Edge AI Server,转化成为“X的Face特色- 进入- A火车站(2018/4/11 20:00)”。也能够是下面提到的简略的APP或者页面级别的“X用户 – 事件1 – A页面(2018/4/11 20:00) ”,对于APP和H5页面来讲,没有计算工作量,只要求埋点格局即可。 ...

August 12, 2020 · 1 min · jiezi

关于lambda:MOor关于Lambda表达式的学习

一、引言本文将探讨 JDK 1.8 中引入的新个性 Lambda () 表达式次要解决如下几个问题: 什么是函数式接口?函数式接口的作用是什么?什么是 Lambda 表达式?Lambda 表达式的作用是什么?Lambda 表达式的演化过程Lambda 表达式的简化过程二、什么是函数式接口接口的办法默认是public abstract,都是形象办法,而函数式接口则有且仅有一个形象办法,但能够有多个非形象办法,通常用注解进行标识 @FunctionalInterface这里列举一些JDK 1.8以前罕用的函数式接口: java.lang.Runnablejava.util.concurrent.Callablejava.util.Comparatorjava.io.FileFilterjava.awt.event.ActionListener在JDK 1.8中新增了如下函数式接口: Consumer(相似于消费者须要传入参数无返回值)Supplier(相似于生产者不须要传入参数,但有返回值)Function(有输出也有返回)Predicate(判断函数,有输出也有返回,返回true or false三、函数式接口的作用是什么?因为Java是面向对象的,办法间参数传递须要为对象,而不能是办法,若想向办法传递办法,则须要一个只有一个形象办法的接口实现类的对象,将这个对象传到办法中就实现了向办法传递办法。总结:函数式接口便是为了解决向办法传递办法的需要,将须要传递的办法转为一个函数式接口对象(有且仅有一个形象办法的接口实现类),以此来解决向办法传递办法的需要。 3.1 进一步再思考一个问题 “为什么要向办法传递办法?”笔者是这样认为的,大部分问题理论是围绕工夫和空间的问题,就像不同的数据结构是在做工夫和空间上的取舍,是为了节约更多空间,而就义一点工夫,或者是为了节约更多工夫,而占用更多空间。用雷同的思考模式去看编程语言的降级其实也是这样的,是为了升高编程难度,使代码更易懂更易用,这样就能够节约更多的工夫去做别的,而不是破费过多工夫去编码。而下面的问题其实也是为了让代码更易懂更易用,至于怎么体现出更易懂更易用的,读者能够单独思考思考。 四、什么是 Lambda 表达式?Lambda表达式能够将函数作为一个办法的参数 五、Lambda 表达式的作用是什么?防止匿名外部类定义过多让代码更简洁,更易读去掉过多冗余、润饰性的代码,只保留外围逻辑这就就好比语法糖,能让代码更好写也更好读。但笔者也晓得,任何新的货色引入必然带来新的凌乱,学习新的常识须要过程,适应也须要过程,但其最终的回报笔者认为是值得的。 六、Lambda 表达式的演变、简化过程// 形式一、函数式接口@FunctionalInterfaceinterface ILike { // 接口中的拜访修饰符、abstract修饰符是能够省略的 // 因为接口中默认就是形象办法,又因为接口是须要实现类来实现其中办法的 // 所以拜访修饰符默认是public void lambda();}// 形式一、实现函数式接口中的形象办法class Like implements ILike { @Override public void lambda() { System.out.println("Lambda 1"); }}public class TestLambda { // 形式二:动态外部类 static class Like1 implements ILike { @Override public void lambda() { System.out.println("Lambda 2"); } } public static void main(String[] args) { // 形式一:函数式接口 ILike like = new Like(); like.lambda(); // 形式二:动态外部类 like = new Like1(); like.lambda(); // 形式三:部分外部类 class Like2 implements ILike { @Override public void lambda() { System.out.println("Lamdba 3"); } } like = new Like2(); like.lambda(); // 形式四:匿名外部类,没有类的名称,必须借助接口或父类 like = new ILike() { @Override public void lambda() { System.out.println("Lamdba 4"); } }; like.lambda(); // 形式五:Lamdba表达式,只保留具体的办法 // 前提是必须为函数式接口 like = () -> { System.out.println("Lambda 5"); }; like.lambda(); }}七、参考Package java.util.function【狂神说Java】多线程详解八、最初若有有余,敬请斧正虚心若愚,求知若渴 ...

July 24, 2020 · 1 min · jiezi

浅谈Kotlin中的函数

本文首发于 vivo互联网技术 微信公众号  链接:https://mp.weixin.qq.com/s/UV23Uw_969oVhiOdo4ZKAw 作者:连凌能Kotlin,已经被Android官方宣布 kotlin first 的存在,去翻 Android 官方文档的时候,发现提供的示例代码已经变成了 Kotlin。Kotlin的务实作风,提供了很多特性帮助开发者减少冗余代码的编写,可以提高效率,也能减少异常。 本文简单谈下Kotlin中的函数,包括表达式函数体,命名参数,默认参数,顶层函数,扩展函数,局部函数,Lambda表达式,成员引用,with/apply函数等。从例子入手,从一般写法到使用特性进行简化,再到原理解析。 1.表达式函数体通过下面这个简单的例子看下函数声明相关的概念,函数声明的关键字是fun,嗯,比JS的function还简单。 Kotlin中参数类型是放在变量:后面,函数返回类型也是。 fun max(a: Int, b: Int) : Int { if (a > b) { return a } else { return b }}当然, Kotlin是有类型推导功能,如果可以根据函数表达式推导出类型,也可以不写返回类型。 但是上面的还是有点繁琐,还能再简单,在 Kotlin中if是表达式,也就是有返回值的,因此可以直接return,另外判断式中只有一行一句也可以省略掉大括号: fun max(a: Int, b: Int) { return if (a > b) a else b}还能在简单点吗?可以,if是表达式,那么就可以通过表达式函数体返回: fun max(a: Int, b: Int) = if(a > b) a else b最终只需要一行代码。 Example 再看下面这个例子,后面会基于这个例子进行修改。这个函数把集合以某种格式输出,而不是默认的toString()。 <T>是泛型,在这里形参集合中的元素都是T类型。返回String类型。fun <T> joinToString( ...

November 4, 2019 · 6 min · jiezi

深入探寻JAVA8-part2浅谈几个内置的函数式接口

前情提要深入探寻JAVA8 part1:函数式编程与Lambda表达式看此文前,不熟悉函数式编程和Lambda表达式的可以先看一下上文回忆一下。 本文将会简单介绍Java8中内置的一些函数式接口 回顾函数式接口函数式接口就是只定义一个抽象方法的接口。在JAVA8以前,就有很多符合函数式接口定义的接口。 //比较器public interface Comparator<T> { int compare(T o1, T o2);}//多线程接口public interface Runnable{ void run();}因为JAVA8中还引入了默认方法的概念,所以即使接口中有多个默认方法,只要接口之定义了一个抽象方法,就满足函数式接口的定义。 JAVA8中对这些可以定义为函数式接口的接口加了一个@FuncationalInterface注解。如果一个接口中定义了多个抽象方法,又添加了这个注解,则会在编译时抛出错误提示。 Consumerpackage java.util.function;import java.util.Objects;@FunctionalInterfacepublic interface Consumer<T> { void accept(T var1); default Consumer<T> andThen(Consumer<? super T> var1) { Objects.requireNonNull(var1); return (var2) -> { this.accept(var2); var1.accept(var2); }; }}这是JAVA8中对Consumer的定义,该函数式接口可以接收一个T类型的数据,并对该数据进行操作。JDK中一个典型的例子是forEach中对Consumer的使用,下面给出了ArrayList中的forEach源码。 @Override public void forEach(Consumer<? super E> consumer) { checkNotNull(consumer); for (E e : array) { consumer.accept(e); } }forEach的接口定义中传入了一个Consumer接口,并且调用Consumer的accept方法对数组中的每个元素进行处理。加入这是一个String数组,则可以使用如下的方式进行调用 list.forEach((String s) -> System.out::println);在Consumer的接口定义中,还有一个andThen的默认方法,后面会再介绍一下这个默认方法。 ...

October 15, 2019 · 2 min · jiezi

什么是lambdalambda表达式你用对了吗

什么是lambda,lambda表达式你用对了吗?Java 8于2014年3月18日发布以来,Lambdas现在已经成为Java环境中熟悉的一部分。带来了期待已久的lambda表达式(又名闭包)特性。它们对我们用Java编程的影响比平台历史上的任何其他变化都要大。什么是lambda表达式?在数学和计算中,lambda表达式通常是一个函数:对于某些或所有输入值的组合,它指定一个输出值。Java中的Lambda表达式将函数的概念引入到语言中。在传统的Java术语中,lambdas可以理解为一种具有更紧凑语法的匿名方法,它还允许省略修饰符、返回类型,在某些情况下还允许省略参数类型。 语法lambda的基本语法是 (parameters) -> expression或者 (parameters) -> { statements; }例子// 1(int x, int y) -> x + y // 接受两个整数并返回它们的和// 2(x, y) -> x - y // 接受两个数字并返回它们的差值// 3() -> 42 // 不接受任何值并返回42// 4(String s) -> System.out.println(s) // 接受一个字符串,将其值打印到控制台,然后什么也不返回//5x -> 2 * x // 接受一个数字,并返回加倍的结果// 6c -> { int s = c.size(); c.clear(); return s; } // 获取一个集合,清除它,并返回它以前的大小笔记参数类型可以显式声明(例1、4),也可以隐式推断(例2、5、6)。声明型和推断型参数不能混合在一个lambda表达式中。主体可以是块(用括号括起来,例6)或表达式(例1-5)。块体可以返回一个值(例6),也可以什么都不返回。在块体中使用或省略return关键字的规则与普通方法体的规则相同。如果主体是一个表达式,它可能返回一个值(例如1、2、3、5)或什么也不返回(例如4)。单个推断类型参数可以省略括号(例如5、6)例6的注释应该被理解为lambda可以作用于一个集合。同样,根据它出现的上下文,它可以作用于其他类型的对象,这些对象具有方法大小和clear,以及适当的参数和返回类型。为什么lambda表达式被添加到Java中?在Java 8中,其目的是为集合提供方法,这些方法将获取函数并以不同的方式使用它们来处理它们的元素。我们将使用一个非常简单的方法forEach作为示例,它获取一个函数并将其应用于每个元素。这种变化带来的好处是集合现在可以在内部组织自己的迭代,将并行化的责任从客户端代码转移到库代码。但是,要让客户机代码利用这一点,需要有一种简单的方法为集合方法提供函数。目前,实现此目的的标准方法是通过适当接口的匿名类实现。但是,用于定义匿名内部类的语法太过笨拙,无法实现这一目的。例如,集合上的forEach方法将获取消费者接口的一个实例,并为每个元素调用它的accept方法: interface Consumer<T> { void accept(T t); } 假设我们要使用forEach来转置java.awt.Point列表中每个元素的x和y坐标。使用匿名内部类实现的消费者,我们将传递在像这样的换位函数: ...

October 8, 2019 · 1 min · jiezi

深入探寻JAVA8-part1函数式编程与Lambda表达式

开篇在很久之前粗略的看了一遍《Java8 实战》。客观的来,说这是一本写的非常好的书,它由浅入深的讲解了JAVA8的新特性以及这些新特性所解决的问题。最近重新拾起这本书并且对书中的内容进行深入的挖掘和沉淀。接下来的一段时间将会结合这本书,以及我自己阅读JDK8源码的心路历程,来深入的分析JAVA8是如何支持这么多新的特性的,以及这些特性是如何让Java8成为JAVA历史上一个具有里程碑性质的版本。 Java8的新特性概览在这个系列博客的开篇,结合Java8实战中的内容,先简单列举一下JAVA8中比较重要的几个新特性: 函数式编程与Lambda表达式Stram流处理Optional解决空指针噩梦异步问题解决方案CompletableFuture颠覆Date的时间解决方案后面将针对每个专题发博进行详细的说明。 简单说一说函数式编程函数式编程的概念并非这两年才涌现出来,这篇文章用一种通俗易懂的方式对函数式编程的理念进行讲解。顾名思义,函数式编程的核心是函数。函数在编程语言中的映射为方法,函数中的参数被映射为传入方法的参数,函数的返回结果被映射为方法的返回值。但是函数式编程的思想中,对函数的定义更加严苛,比如参数只能被赋值一次,即参数必须为final类型,在整个函数的声明周期中不能对参数进行修改。这个思想在如今看来是不可理喻的,因为这意味着任何参数的状态都不能发生变更。 那么函数式编程是如何解决状态变更的问题呢?它是通过函数来实现的。下面给了一个例子: String reverse(String arg) { if(arg.length == 0) { return arg; } else { return reverse(arg.substring(1, arg.length)) + arg.substring(0, 1); }}对字符串arg进行倒置并不会修改arg本身,而是会返回一个全新的值。它完全符合函数式编程的思想,因为在整个函数的生命周期中,函数中的每一个变量都没有发生修改。这种不变行在如今称为Immutable思想,它极大的减少了函数的副作用。这一特性使得它对单元测试,调试以及编发编程极度友好。因此在面向对象思想已经成为共识的时代,被重新提上历史的舞台。 但是,编程式思想并不只是局限于此,它强调的不是将所有的变量声明为final,而是将这种可重入的代码块在整个程序中自由的传递和复用。JAVA中是通过对象的传递来实现的。举个例子,假如现在有一个筛选订单的功能,需要对订单从不同的维度进行筛选,比如选出所有已经支付完成的订单,或是选出所有实付金额大于100的订单。 简化的订单模型如下所示: public class Order{ private String orderId; //实付金额 private long actualFee; //订单创建时间 private Date createTime; private boolean isPaid}接着写两段过滤逻辑分别实现选出已经支付完成的订单,和所有实付金额大于100的订单 //选出已经支付完成的订单public List<Order> filterPaidOrder(List<Order> orders) { List<Order> paidOrders = new ArrayList<>(); for(Order order : orders) { if(order.isPaid()) { paidOrders.add(order); } } return paidOrdres;}//选出实付金额大于100的订单public List<Order> filterByFee(List<Order> orders) { List<Order> resultOrders = new ArrayList<>(); for(Order order : orders) { if(order.getActualFee()>100) { resultOrders.add(order); } } return resultOrders;}可以看到,上面出现了大量的重复代码,明显的违背了DRY(Dont Repeat Yourself)原则,可以先通过模板模式将判断逻辑用抽象方法的形式抽取出来,交给具体的子类来实现。代码如下: ...

October 7, 2019 · 2 min · jiezi

Java87自制多糖-switch

背景JDK 12 和 JDK 13 已经发布了,伴随着许多对 Java 语法的小改进,比如我们非常熟悉的 switch: JDK12 之前switch (type) { case "all": System.out.println("列出所有帖子"); break; case "auditing": System.out.println("列出审核中的帖子"); break; case "accepted": System.out.println("列出审核通过的帖子"); break; case "rejected": System.out.println("列出审核不通过的帖子"); break; default: System.out.println("参数'type'错误,请检查"); break;}JDK12switch (type) { case "all" -> System.out.println("列出所有帖子"); case "auditing" -> System.out.println("列出审核中的帖子"); case "accepted" -> System.out.println("列出审核通过的帖子"); case "rejected" -> System.out.println("列出审核不通过的帖子"); default -> System.out.println("参数'type'错误,请检查");}JDK13String value = switch (i) { case 0 -> "zero" case 1 -> "one" case 2 -> "two" default -> "many"};新特性很美好,但是如今在业界最流行的版本依然是 JDK8,所以想要在生产环境用上这么舒服的 switch,目前看来还是遥遥无期。好在我们还有 Lambda,正所谓 “自己动手,丰衣足食”,我们来试试能不能自己做出一个和 JDK12 & JDK13 的 swtich 类似的东西,好给我们平淡的编码生活,加点糖。 ...

October 5, 2019 · 4 min · jiezi

java8-lambda表达式二方法引用

当我们在使用lambda去表示某个函数式接口的实例时,需要在lambda表达式的主体里去编写函数式接口抽象方法的实现,如果在现有的类中已经存在与抽象方法类似的方法了,我们希望直接引用现有的方法,而不用再去重新写实现了。方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们。 方法引用和lambda表达式拥有相同的特性,它们都需要代表一个目标类型,并需要被转化为函数式接口的实例,不过我们并不需要为方法引用提供方法体,我们可以直接通过方法名称引用已有方法方。方法引用要使用到操作符“::”,左边是类名或者对象名,右边是方法名或者关键字new 方法引用分类首先被引用方法的返回值类型要和函数式接口抽象方法的返回值类型一致,至于参数列表要根据每种引用方式而定。 静态方法引用引用语法:ClassName::staticMethodName//Function<String, Long> f = x -> Long.valueOf(x);Function<String, Long> f = Long::valueOf;Long result = f.apply("10");静态方法引用时,静态方法要与函数式接口抽象方法参数列表一致 类型的实例方法引用引用语法:ClassName::methodName//BiPredicate<String, String> bpredicate = (x,y) -> x.equals(y);BiPredicate<String, String> bpredicate = String::equals;boolean result = bpredicate.test("abc", "abcd");//ToIntFunction<String> f = (s) -> s.length();ToIntFunction<String> f = String::length;int result2 = f.applyAsInt("hello");类型的实例方法引用时,函数式接口抽象方法的第一个参数是被引用方法的调用者,第二个参数(或者无参)是被引用方法的参数 外部对象方法引用引用语法:objectName::methodNameList<String> list = Arrays.asList("hello", "world", "aaa");//Predicate<String> p = (s) -> list.contains(s);Predicate<String> p = list::contains; //list是lambda的一个外部对象boolean result = p.test("aaa");外部对象方法引用时,被引用方法与函数式接口抽象方法参数列表一致 构造器引用引用语法:ClassName::new//Function<Long, Date> fun = (number) -> new Date(number);Function<Long, Date> fun = Date::new;Date date = fun.apply(1000000000000L);构造器引用时,被引用的构造方法与函数式接口抽象方法参数列表一致 ...

September 6, 2019 · 1 min · jiezi

函数式编程让你忘记设计模式

本文是一篇《Java 8实战》的阅读笔记,阅读大约需要5分钟。有点标题党,但是这确实是我最近使用Lambda表达式的感受。设计模式是过去的一些好的经验和套路的总结,但是好的语言特性可以让开发者不去考虑这些设计模式。面向对象常见的设计模式有策略模式、模板方法、观察者模式、责任链模式以及工厂模式,使用Lambda表达式(函数式编程思维)有助于避免面向对象开发中的那些固定代码。下面我们挑选了策略模式和职责链模式两个案例进行分析。 案例1:策略模式 当我们解决一个问题有不同的解法的时候,又不希望客户感知到这些解法的细节,这种情况下适合使用策略模式。策略模式包括三个部分: 解决问题的算法(上图中的Strategy);一个或多个该类算法的具体实现(上图中的ConcreteStrategyA、ConcreteStrategyB和ConcreteStrategyC)一个或多个客户使用场景(上图中的ClientContext)面向对象思路首先定义策略接口,表示排序策略: public interface ValidationStrategy { boolean execute(String s);}然后定义具体的实现类(即不同的排序算法): public class IsAllLowerCase implements ValidationStrategy { @Override public boolean execute(String s) { return s.matches("[a-z]+"); }}public class IsNumberic implements ValidationStrategy { @Override public boolean execute(String s) { return s.matches("\\d+"); }}最后定义客户使用场景,代码如下图所示。Validator是为客户提供服务时使用的上下文环境,每个Valiator对象中都封装了具体的Strategy对象,在实际工作中,我们可以通过更换具体的Strategy对象来进行客户服务的升级,而且不需要让客户进行升级。 public class Validator { private final ValidationStrategy strategy; public Validator(ValidationStrategy strategy) { this.strategy = strategy; } /** * 给客户的接口 */ public boolean validate(String s) { return strategy.execute(s); }}public class ClientTestDrive { public static void main(String[] args) { Validator numbericValidator = new Validator(new IsNumberic()); boolean res1 = numbericValidator.validate("7780"); System.out.println(res1); Validator lowerCaseValidator = new Validator(new IsAllLowerCase()); boolean res2 = lowerCaseValidator.validate("aaaddd"); System.out.println(res2); }}函数式编程思路如果使用Lambda表达式考虑,你会发现ValidationStrategy就是一个函数接口(还与Predicate<String>具有同样的函数描述),那么就不需要定义上面那些实现类了,可以直接用下面的代码替换,原因是Lambda表达式内部已经对这些类进行了一定的封装。 ...

July 7, 2019 · 2 min · jiezi

questioning-the-lambda-architecture

原文:https://www.oreilly.com/ideas... Storm的作者Nathan Marz提出了 lambda 架构,该架构是在 MapReduce 上和 Storm 上构建流式处理的应用。lambda 架构是捕获不可变的数据序列并将其并行的发送给批处理系统和流式处理系统。但是你需要分别在批处理系统和流式处理系统中实现一次数据处理逻辑。而在查询的时候需要将两个系统计算的结果合并在一起,以完成查询返回给请求端。 对于两种处理系统,你可以灵活替换实现系统,比如使用 kafka+storm 实现流出处理,使用 hadoop 实现批式处理,输出结果通常在分开的两个数据库中,一个是为了流式而优化,另一个是为了批式更新而优化。 但是对于Jay Kreps【原文作者】因为一直在从事实时数据管道的建设,虽然其中有一些风格就是 lambda 架构,但是他更喜欢一个新的替换的方案。 Lambda 架构图 为什么会提出lambda 架构呢因为那些试图构建流式处理系统的人并没有过多的考虑数据重计算的问题,最终造成系统没有便利的方法来处理数据重计算。 lambda 架构强调保持输入的原始数据不可变并且显示的将数据重新计算的问题给展现了出来。通过 lambda 架构可以比较好的解决了流式数据和历史数据的计算问题。 为什么数据会可能重新计算呢?因为随着时间的推移,代码可能会改变。改变的原因可能是你想在输出结果中新加一个字段,或者是因为代码有 bug 需要修复。不管是什么原因,要使得历史数据得到新的预期结果,就需要将数据重新计算。 Jay Kreps对 lambda 架构的反驳因为 lambda 架构提出其中一个观点认为流式系统是近似的,不准确的,准确度不如批式处理。Jay Kreps对此观点不敢苟同,他认为现存的流式处理的框架不如 MapReduce成熟,但不代表流式系统不能如批式系统那样提供强大的语义保证。而且 lambda 架构提出的标题是"beats the CAP theorem",即干掉 CAP 理论,但是实际上尽管在流处理上对延时和可用性间存在权衡,但因为这是一种异步处理架构。所以异步计算的结果也不能立即保持与输入的数据一致,因此 CAP 理论仍然没有被打破。 lambda 架构存在的问题是什么?lambda 架构需要维护在两个复杂的分布式系统中输出相同结果的代码,就像看起来那么痛苦,而且Jay Kreps也不认为该问题是可以解决的。 因为 storm 和 Hadoop 分布式框架非常的复杂,因此不可避免的代码会针对其运行的框架进行设计。 为什么 Lambda 会这样令人兴奋呢?Jay Kreps建议如果对延时性不敏感就仅使用如 MapReduce 这样的批处理系统。如果延迟敏感则使用流式处理框架,除非特别必须才同时使用这两种系统。 但是需求总是千奇百怪的,人们需要构建复杂的,低延时的处理系统,(而且在天朝 PM 都想要大而全的功能下,这样需求更盛)。 他们拥有的两件事情并不能解决他们的问题:一个可以处理历史数据的可扩展高延迟批处理系统和一个无法重新处理结果的低延迟流处理系统。但通过将两个东西连接在一起,实际上构成了一个可行的方案,也就是 lambda 架构。但尽管lambda 架构让人很痛苦,但确实也解决了重新计算这样通常让人忽略的问题。但是Jay Kreps只认为 lambda 架构只是临时解决方案,它不是新的编程范例也不是大数据的未来方向。 ...

June 25, 2019 · 1 min · jiezi

修炼内功Java8-Lambda究竟是不是匿名类的语法糖

本文已收录【修炼内功】跃迁之路 初次接触Java8的时候感觉Lambda表达式很神奇(Lambda表达式带来的编程新思路),但又总感觉它就是匿名类或者内部类的语法糖而已,只是语法上更为简洁罢了,如同以下的代码 public class Lambda { private static void hello(String name, Consumer<String> printer) { printer.accept(name); } public static void main(String[] args) { hello("lambda", (name) -> System.out.println("Hello " + name)); hello("匿名类", new Consumer<String> () { @Override public void accept(String name) { System.out.println("Hello " + name); } }); hello("内部类", new SupplierImpl()); } static class SupplierImpl implements Consumer<String> { @Override public void accept(String name) { System.out.println("Hello " + name); } }}编译后会产生三个文件 ...

June 25, 2019 · 2 min · jiezi

修炼内功JVM-虚拟机视角的方法调用

本文已收录【修炼内功】跃迁之路 『我们写的Java方法在被编译为class文件后是如何被虚拟机执行的?对于重写或者重载的方法,是在编译阶段就确定具体方法的么?如果不是,虚拟机在运行时又是如何确定具体方法的?』 方法调用不等于方法执行,一切方法调用在class文件中都只是常量池中的符号引用,这需要在类加载的解析阶段甚至到运行期间才能将符号引用转为直接引用,确定目标方法进行执行 在编译过程中编译器并不知道目标方法的具体内存地址,因此编译器会暂时使用符号引用来表示该目标方法编译代码 public class MethodDescriptor { public void printHello() { System.out.println("Hello"); } public void printHello(String name) { System.out.println("Hello " + name); } public static void main(String[] args) { MethodDescriptor md = new MethodDescriptor(); md.printHello(); md.printHello("manerfan"); }}查看其字节码 main方法中调用两次不同的printHello方法,对应class文件中均为invokevirtual指令,分别调用常量池中的#12及#14,查看常量池 #12及#14对应两个Methodref方法引用,这两个方法引用均为符号引用(使用方法描述符)而并非直接引用 虚拟机识别方法的关键在于类名、方法名及方法描述符(method descriptor),方法描述符由方法的参数类型及返回类型构成 方法名及方法描述符在编译阶段便可以确定,但对于实际类名,一些场景下(如类继承)只有在运行时才可知 方法调用指令目前Java虚拟机里提供了5中方法调用的字节码指令 invokestatic: 调用静态方法invokespecial: 调用实例构造器<init>方法、私有方法及父类方法invokevirtual: 调用虚方法(会在运行时确定具体的方法对象)invokeinterface: 调用接口方法(会在运行时确定一个实现此接口的对象)invokedynamic: 先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法invokestatic及invokespecial调用的方法(静态方法、构造方法、私有方法、父类方法),均可以在类加载的解析阶段确定唯一的调用版本,从而将符号引用直接解析为该方法的直接引用,这些方法称之为非虚方法 而invokevirtual及invokeinterface调用的方法(final方法除外,下文提到),在解析阶段并不能唯一确定,只有在运行时才能拿到实际的执行类从而确定唯一的调用版本,此时才可以将符号引用转为直接引用,这些方法称之为虚方法 invokedynamic比较特殊,单独分析 简单示意,如下代码 public interface MethodBase { String getName();}public class BaseMethod implements MethodBase { @Override public String getName() { return "manerfan"; } public void print() { System.out.println(getName()); }}public class MethodImpl extends BaseMethod { @Override public String getName() { return "maner-fan"; } @Override public void print() { System.out.println("Hello " + getName()); }; public String getSuperName() { return super.getName(); } public static String getDefaultName() { return "default"; }}public class MethodDescriptor { public static void print(BaseMethod baseMethod) { baseMethod.print(); } public static String getName(MethodBase methodBase) { return methodBase.getName(); } public static void main(String[] args) { MethodImpl.getDefaultName(); MethodImpl ml = new MethodImpl(); ml.getSuperName(); getName(ml); print(ml); }}查看MethodDescriptor的字节码 ...

June 23, 2019 · 4 min · jiezi

lambda

lambda表达式是什么lambda表达式是一个代码块,并且可以执行很多次。支持函数式编程。 语法我们之前说lambda表达式是函数式编程,我们学习lambda可以当做一个函数来进行对比去学习。 语法如下: 参数 -> 表达式具体解读 1.参数: 如果没有有一个参数,需要提供中括号() -> System.out.println("hello world");如果只有一个参数,而且这个参数类型可以推导出,可以不提供中括号,也可以提供(String first) -> return first.toString();String first -> return first.toString();如果可以根据上下文推导出参数的类型,则可以不提供参数类型(first) -> return first.toString();2.表达式 无需指定lambda表达式的返回类型,lambda表达式会根据上下文推导出(String first, String second) -> first.length() - second.length();方法引用stream.map(Sysout.out::println)构造器引用stream.map(String::new)变量作用域lambda表达式可以使用外部的变量,但是这个变量在传入过来的时候不能是变化的或者不能在内部进行变化的。 函数式接口我们一直依赖传递参数都是一个对象,那么传递lambda表达式可不可以呢?当然可以,但是我们不能将lambda表达式传递给对象 那么我怎么定义lambda表达式作为函数接口的方法呢?下面给出一个表提供了方式 函数式接口参数类型返回类型抽象方法名描述其他方法Runnable无voidrun作为无参数或返回值 Supplier<T>无Tget提供一个T类型的值返回 Consumer<T>Tvoidaccept处理一个T类型的参数andThenBiConsumer<T,U>T,Uvoidaccept处理一个T和U的参数andThenFuntion<T,R>TRapply处理一个T的参数,返回一个R的值compose,andThen,identityBiFunction<T,U,R>T,URapply处理一个T和R的参数,返回一个R的值andThenUnaryOperator<T>TTapply类型T上的一元操作符"compose,andThen,IdentityBinaryOperator<T>T,TTapply类型T上的二元操作符andThen,maxBy,minByPredicate<T>Tbooleantest布尔值函数and,or,negate,isEqualBiPredicate<T,U>T,Ubooleantest有两个参数的布尔值函数and,or,negate流(Stream)的规则流不存储其元素流的操作不会修改其数据源。例如,filter方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含过滤掉的流。流的操作是尽可能惰性的。只有遇到想要的结果才会执行,完成之后流就不能操作了。API创建Stream -static <T> Stream<T> of(T... values) 产生一个元素为给定值的流 static <T> Stream<T> empty()产生一个不包含任何元素的流 static <T> Stream<T> generate(Supplier<T> s)产生一个无限流,它的值是通过反复调用函数构成 static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)产生一个无限流,它的元素包含种子,在种子调用f产生的值 default Stream<E> steam()default Stream<E> parallelStream()产生当前集合中所有元素的顺序流或并行流 static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive)产生一个流,元素数组里面指定的开始到结束的部分 ...

June 23, 2019 · 2 min · jiezi

利用Lambda实现通过gettersetter方法引用拿到属性名

有很多开发场景需要用到Java Bean的属性名,直接写死属性名字符串的形式容易产生bug(属性名一旦变化,IDE不会告诉你你的字符串需要同步修改)。JDK8的Lambda可以通过方法引用简化代码,同样也可以通过getter/setter的方法引用拿到属性名,避免潜在的bug。1. 定义FunctionalInterface 接收方法引用/** * getter方法接口定义 */@FunctionalInterfacepublic interface IGetter<T> extends Serializable { Object apply(T source);}/** * setter方法接口定义 */@FunctionalInterfacepublic interface ISetter<T, U> extends Serializable { void accept(T t, U u);}2. 定义getter/setter引用转换属性名的工具类public class BeanUtils { ... /** * 缓存类-Lambda的映射关系 */ private static Map<Class, SerializedLambda> CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>(); /*** * 转换方法引用为属性名 * @param fn * @return */ public static <T> String convertToFieldName(IGetter<T> fn) { SerializedLambda lambda = getSerializedLambda(fn); String methodName = lambda.getImplMethodName(); String prefix = null; if(methodName.startsWith("get")){ prefix = "get"; } else if(methodName.startsWith("is")){ prefix = "is"; } if(prefix == null){ log.warn("无效的getter方法: "+methodName); } // 截取get/is之后的字符串并转换首字母为小写(S为diboot项目的字符串工具类,可自行实现) return S.uncapFirst(S.substringAfter(methodName, prefix)); } /*** * 转换setter方法引用为属性名 * @param fn * @return */ public static <T,R> String convertToFieldName(ISetter<T,R> fn) { SerializedLambda lambda = getSerializedLambda(fn); String methodName = lambda.getImplMethodName(); if(!methodName.startsWith("set")){ log.warn("无效的setter方法: "+methodName); } // 截取set之后的字符串并转换首字母为小写(S为diboot项目的字符串工具类,可自行实现) return S.uncapFirst(S.substringAfter(methodName, "set")); } /*** * 获取类对应的Lambda * @param fn * @return */ private static SerializedLambda getSerializedLambda(Serializable fn){ //先检查缓存中是否已存在 SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass()); if(lambda == null){ try{//提取SerializedLambda并缓存 Method method = fn.getClass().getDeclaredMethod("writeReplace"); method.setAccessible(Boolean.TRUE); lambda = (SerializedLambda) method.invoke(fn); CLASS_LAMDBA_CACHE.put(fn.getClass(), lambda); } catch (Exception e){ log.error("获取SerializedLambda异常, class="+fn.getClass().getSimpleName(), e); } } return lambda; }}3. 开心的引用// 方法引用替代hard code字符串,属性名变化时IDE会同步提示String ITEM_NAME = BeanUtils.convertToFieldName(User::getOrgName);// 等同于 String ITEM_NAME = "orgName";Diboot - 简单高效的轻代码开发框架 ...

June 4, 2019 · 1 min · jiezi

乐字节Java8核心特性实战之Lambda表达式

Java8 引入Lambda表达式,允许开发者将函数当成参数传递给某个方法,或者把代码本身当作数据进行处理。使用Lambda表达式,使得应用变得简洁而紧凑。 很多语言(Groovy、Scala等)从设计之初就支持Lambda表达式。但是java中使用的是 匿名内部类代替。最后借助强大的社区力量,找了一个折中的Lambda实现方案,可以实现简洁而紧凑的语言结构。 一、匿名内部类到Lambda的演化匿名内部类,即一个没有名字的,存在于一个类或方法内部的类。当我们需要用某个类且只需要用一次,创建和使用和二为一时,我们可以选择匿名内部类,省掉我们定义类的步骤。 匿名内部类会隐士的继承一个类或实现一个接口,或者说匿名内部类是一个继承了该类或者实现了该接口的子类匿名对象。下面看一个匿名内部类的例子 package com.java8;/* 定义和使用匿名内部类 */public class NoNameClass { public static void main(String[] args) { Model m = new Model(){ @Override public void func() { System.out.println("方法的实现"); } }; m.func(); }}// 需要被实现的接口interface Model{ void func();}等价的Lambda 代码 package com.java8;/* 定义和使用Lambda 简化代码 */public class NoNameClass { public static void main(String[] args) { Model m = new Model(){()->{ System.out.println("方法的实现"); }}; m.func(); }}可以看出使用Lambda 表达式替代了匿名内部类代码,使得代码更加简化、紧凑。 1、语法 (parameters) -> expression  或  (parameters) ->{ statements; } 可选类型声明不需要声明参数类型,编译器可以统一识别参数值。可选的参数圆括号一个参数无需定义圆括号,但多个参数需要定义圆括号。可选的大括号如果主体包含了一个语句,就不需要使用大括号。可选的返回关键字如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指明表达式返回了一个数值2、Lambda 表达式示例 ...

June 1, 2019 · 2 min · jiezi

【修炼内功】[Java8] Lambda表达式里的陷阱

本文已收录【修炼内功】跃迁之路Lambdab表达式带来的好处就不再做过多的介绍了,这里重点介绍几点,在使用Lambda表达式过程中可能遇到的"陷阱"Effectively Final在使用Lambda表达式的过程中,经常会遇到如下的问题图中的sayWords为什么一定要是final类型,effectively final又是什么?但,如果改为如下,貌似问题又解决了似乎,只要对sayWords不做变动就可以如果将sayWords从方法体的变量提到类的属性中,情况又会有变化,即使对sayWords有更改,也会编译通过难道,就是因为局部变量和类属性的区别?在Java 8 in Action一书中有这样一段话You may be asking yourself why local variables have these restrictions. First, there’s a key difference in how instance and local variables are implemented behind the scenes. Instance variables are stored on the heap, whereas local variables live on the stack. If a lambda could access the local variable directly and the lambda were used in a thread, then the thread using the lambda could try to access the variable after the thread that allocated the variable had deallocated it. Hence, Java implements access to a free local variable as access to a copy of it rather than access to the original variable. This makes no difference if the local variable is assigned to only once—hence the restriction. Second, this restriction also discourages typical imperative programming patterns (which, as we explain in later chapters, prevent easy parallelization) that mutate an outer variable.首先,要理解Local Variables和Instance Variables在JVM内存中的区别Local Variables随Thread存储在Stack栈内存中,而Instance Variables则随Instance存储在Heap堆内存中Local Variables的回收取决于变量的作用域,程序的运行一旦超出变量的作用域,该内存空间便被立刻回收另作他用Instance Variables的回收取决于引用数,当再没有引用的时候,便会在一个"合适"的时间被JVM垃圾回收器回收试想,如果Lambda表达式引用了局部变量,并且该Lambda表达式是在另一个线程中执行,那在某种情况下该线程则会在该局部变量被收回后(函数执行完毕,超出变量作用域)被使用,显然这样是不正确的;但如果Lambda表达式引用了类变量,则该类(属性)会增加一个引用数,在线程执行完之前,引用数不会归为零,也不会触发JVM对其的回收操作但这解释不了图2的情况,同样是局部变量,只是未对sayWords做改动,也是可以通过编译的,这里便要介绍effectively finalBaeldung大神的博文中有这样一段话Accessing a non-final variable inside lambda expressions will cause the compile-time error. But it doesn’t mean that you should mark every target variable as final.According to the “effectively final” concept, a compiler treats every variable as final, as long as it is assigned only once.It is safe to use such variables inside lambdas because the compiler will control their state and trigger a compile-time error immediately after any attempt to change them.其中提到了 assigned only once,字面理解便是只赋值了一次,对于这种情况,编译器便会 treats variable as final,对于只赋值一次的局部变量,编译器会将其认定为effectively final,其实对于effectively final的局部变量,Lambda表达式中引用的是其副本,而该副本的是不会发生变化的,其效果就和final是一致的Throwing ExceptionJava的异常分为两种,受检异常(Checked Exception)和非受检异常(Unchecked Exception)Checked Exception, the exceptions that are checked at compile time. If some code within a method throws a checked exception, then the method must either handle the exception or it must specify the exception using throws keyword.Unchecked Exception, the exceptions that are not checked at compiled time. It is up to the programmers to be civilized, and specify or catch the exceptions.简单的讲,受检异常必须使用try…catch进行捕获处理,或者使用throws语句表明该方法可能抛出受检异常,由调用方进行捕获处理,而非受检异常则不用。受检异常的处理是强制的,在编译时检测。在Lambda表达式内部抛出异常,我们该如何处理?Unchecked Exception首先,看一段示例public class Exceptional { public static void main(String[] args) { Stream.of(3, 8, 5, 6, 0, 2, 4).forEach(lambdaWrapper(i -> System.out.println(15 / i))); } private static Consumer<Integer> lambdaWrapper(IntConsumer consumer) { return i -> consumer.accept(i); }}该段代码是可以编译通过的,但运行的结果是> 5> 1> 3> 2> Exception in thread “main” java.lang.ArithmeticException: / by zero at Exceptional.lambda$main$0(Exceptional.java:13) at Exceptional.lambda$lambdaWrapper$1(Exceptional.java:17) at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580) at Exceptional.main(Exceptional.java:13)由于Lambda内部计算时,由于除数为零抛出了ArithmeticException异常,导致流程中断,为了解决此问题可以在lambdaWrapper函数中加入try…catchprivate static Consumer<Integer> lambdaWrapper(IntConsumer consumer) { return i -> { try { consumer.accept(i); } catch (ArithmeticException e) { System.err.println(“Arithmetic Exception occurred : " + e.getMessage()); } };}再次运行> 5> 1> 3> 2> Arithmetic Exception occurred : / by zero> 7> 3对于Lambda内部非受检异常,只需要使用try…catch即可,无需做过多的处理Checked Exception同样,一段示例public class Exceptional { public static void main(String[] args) { Stream.of(3, 8, 5, 6, 0, 2, 4).forEach(lambdaWrapper(i -> writeToFile(i))); } private static Consumer<Integer> lambdaWrapper(IntConsumer consumer) { return i -> consumer.accept(i); } private static void writeToFile(int integer) throws IOException { // logic to write to file which throws IOException }}由于IOException为受检异常,该段将会程序编译失败按照Unchecked Exception一节中的思路,我们在lambdaWrapper中使用try…catch处理异常private static Consumer<Integer> lambdaWrapper(IntConsumer consumer) { return i -> { try { consumer.accept(i); } catch (IOException e) { System.err.println(“IOException Exception occurred : " + e.getMessage()); } };}但出乎意料,程序依然编译失败查看IntConsumer定义,其并未对接口accept声明异常@FunctionalInterfacepublic interface IntConsumer { /** * Performs this operation on the given argument. * * @param value the input argument / void accept(int value);}为了解决此问题,我们可以自己定义一个声明了异常的ThrowingIntConsumer@FunctionalInterfacepublic interface ThrowingIntConsumer<E extends Exception> { /* * Performs this operation on the given argument. * * @param value the input argument * @throws E / void accept(int value) throws E;}改造代码如下private static Consumer<Integer> lambdaWrapper(ThrowingIntConsumer<IOException> consumer) { return i -> { try { consumer.accept(i); } catch (IOException e) { System.err.println(“IOException Exception occurred : " + e.getMessage()); } };}但,如果我们希望在出现异常的时候终止流程,而不是继续运行,可以在获取到受检异常后抛出非受检异常private static Consumer<Integer> lambdaWrapper(ThrowingIntConsumer<IOException> consumer) { return i -> { try { consumer.accept(i); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e.getCause()); } };}所有使用了ThrowingIntConsumer的地方都需要写一遍try…catch,有没有优雅的方式?或许可以从ThrowingIntConsumer下手@FunctionalInterfacepublic interface ThrowingIntConsumer<E extends Exception> { /* * Performs this operation on the given argument. * * @param value the input argument * @throws E / void accept(int value) throws E; /* * @return a IntConsumer instance which wraps thrown checked exception instance into a RuntimeException */ default IntConsumer uncheck() { return i -> { try { accept(i); } catch (final E e) { throw new RuntimeException(e.getMessage(), e.getCause()); } }; }}我们在ThrowingIntConsumer中定义了一个默认函数uncheck,其内部会自动调用Lambda表达式,并在捕获到异常后将其转为非受检异常并重新抛出此时,我们便可以将lambdaWrapper函数优化如下private static Consumer<Integer> lambdaWrapper(ThrowingIntConsumer<IOException> consumer) { return i -> consumer.accept(i).uncheck();}unCheck会将IOException异常转为RuntimeException抛出有没有更优雅一些的方式?由于篇幅原因不再过多介绍,感兴趣的可以参考 throwing-function 及 Vavrthis pointerJava中,类(匿名类)中都可以使用this,Lambda表达式也不例外public class ThisPointer { public static void main(String[] args) { ThisPointer thisPointer = new ThisPointer(“manerfan”); new Thread(thisPointer.getPrinter()).start(); } private String name; @Getter private Runnable printer; public ThisPointer(String name) { this.name = name; this.printer = () -> System.out.println(this); } @Override public String toString() { return “hello " + name; }}在ThisPointer类的构造函数中,使用Lambda表达式定义了printer属性,并重写了类的toString方法运行后结果> hello manerfanThisPointer类的构造函数中,将printer属性的定义改为匿名类public class ThisPointer { public static void main(String[] args) { ThisPointer thisPointer = new ThisPointer(“manerfan”); new Thread(thisPointer.getPrinter()).start(); } private String name; @Getter private Runnable printer; public ThisPointer(String name) { this.name = name; this.printer = new Runnable() { @Override public void run() { System.out.println(this); } }; } @Override public String toString() { return “hello " + name; }}重新运行后结果> ThisPointer$1@782b1823可见,Lambda表达式及匿名类中的this指向的并不是同一内存地址这里我们需要理解,在Lambda表达式中它在词法上绑定到周围的类 (定义该Lambda表达式时所处的类),而在匿名类中它在词法上绑定到匿名类Java语言规范在15.27.2描述了这种行为Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).The transparency of this (both explicit and implicit) in the body of a lambda expression – that is, treating it the same as in the surrounding context – allows more flexibility for implementations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution.Practically speaking, it is unusual for a lambda expression to need to talk about itself (either to call itself recursively or to invoke its other methods), while it is more common to want to use names to refer to things in the enclosing class that would otherwise be shadowed (this, toString()). If it is necessary for a lambda expression to refer to itself (as if via this), a method reference or an anonymous inner class should be used instead.那,如何在匿名类中如何做到Lambda表达式的效果,获取到周围类的this呢?这时候就必须使用qualified this了,如下public class ThisPointer { public static void main(String[] args) { ThisPointer thisPointer = new ThisPointer(“manerfan”); new Thread(thisPointer.getPrinter()).start(); } private String name; @Getter private Runnable printer; public ThisPointer(String name) { this.name = name; this.printer = new Runnable() { @Override public void run() { System.out.println(ThisPointer.this); } }; } @Override public String toString() { return “hello " + name; }}运行结果如下> hello manerfan其他在排查问题的时候,查看异常栈是必不可少的一种方法,其会记录异常出现的详细记录,包括类名、方法名行号等等信息那,Lambda表达式中的异常栈信息是如何的?public class ExceptionStack { public static void main(String[] args) { new ExceptionStack().run(); } private Function<Integer, Integer> divBy100 = divBy(100); void run() { Stream.of(1, 7, 0, 6).filter(this::isEven).map(this::div).forEach(System.out::println); } boolean isEven(int i) { return 0 == i / 2; } int div(int i) { return divBy100.apply(i); } Function<Integer, Integer> divBy(int div) { return i -> div / i; }}这里我们故意制造了一个ArithmeticException,并且增加了异常的栈深,运行后的异常信息如下Exception in thread “main” java.lang.ArithmeticException: / by zero at ExceptionStack.lambda$divBy$0(ExceptionStack.java:30) at ExceptionStack.div(ExceptionStack.java:26) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175) at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418) at ExceptionStack.run(ExceptionStack.java:18) at ExceptionStack.main(ExceptionStack.java:12)异常信息中的ExceptionStack.lambda$divBy$0 ReferencePipeline$3$1.accept等并不能让我们很快地了解,具体是类中哪个方法出现了问题,此类问题在很多编程语言中都存在,也希望JVM有朝一日可以彻底解决关于Lambda表达式中的"陷阱"不仅限于此,也希望大家能够一起来讨论 ...

April 13, 2019 · 6 min · jiezi

乐字节-Java8新特性-Lambda表达式

上一篇文章我们了解了Java8新特性-接口默认方法,接下来我们聊一聊Java8新特性之Lambda表达式。Lambda表达式(也称为闭包),它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理。很多语言(Groovy、Scala等)从设计之初就支持Lambda表达式。但是java中使用的是 匿名内部类代替。最后借助强大的社区力量,找了一个折中的Lambda实现方案,可以实现简洁而紧凑的语言结构。1、匿名内部类到Lambda的演化匿名内部类,即一个没有名字的,存在于一个类或方法内部的类。当我们需要用某个类且只需要用一次,创建和使用和二为一时,我们可以选择匿名内部类,省掉我们定义类的步骤。匿名内部类会隐士的继承一个类或实现一个接口,或者说匿名内部类是一个继承了该类或者实现了该接口的子类匿名对象。下面看一个匿名内部类的例子:测试类中调用方法package com.lotbyte.main;/* 定义和使用匿名内部类 */public class NoNameClass { public static void main(String[] args) { Model m = new Model(){ @Override public void func() { System.out.println(“方法的实现”); } }; m.func(); }}// 需要被实现的接口interface Model{ void func();}2、Lambda快速使用从某种意义上来说,Lambda表达式可以看作是匿名内部类对象的简写形式。最简单的Lambda表达式可以由 用逗号分隔的参数列表、->符号和语句块组成。注意:此时匿名内部类只能实现接口,不能是继承抽象类.例如将上面的例子做一个简化,使用Lambda的形式如下:public class NonameClassForLambda { public static void main(String[] args) { // Lambda方式简写,方法实现可以很简单 Model1 md = ()-> System.out.println(“hello”); md.func(); // 也可以是比较复杂的操作 md = () -> { for (int i = 1; i <=5; i++) { System.out.println(i); } }; md.func(); }}// 接口interface Model1{ void func();}以上是一个简单的Lambda的书写形式,()中是形参列表,没有则为空括号, ->为语法格式,之后则为方法的实现(一条语句可以直接书写,当有多条语句时,需要使用{}进行包裹)。从这可以看出在接口中必须只能存在一个抽象方法。注意:Lambda中必须有个接口3、Lambda的形式使用Lambda时,实现方法可以有参数,也可以有返回值,如果没指定参数类型,则由编译器自行推断得出。3.1、 无参带返回值生成[1,10]之间的任意整数interface Model2{ int func();}Model2 md2 = () -> {return (int)(Math.random()10+1)};说明:Lambda的改写需要有对应的抽象方法,当没有参数时需要使用()占位,当表达式只有一行代码时,可以省略return和{}以上的Lambda等价于: Model2 md2 = () -> (int)(Math.random()10+1);3.2 、带参带返回值返回一个对数字描述的字符串:interface Model3{ String func(int a);}Model3 md3 = (int a) -> { return “This is a number " + a;};说明:形参写在()内即可,参数的类型可以省略,此时将由编译器自行推断得出,同时还可以省略() md3 = a -> “This is a number " + a;省略了参数类型,小括号,同时连带实现体的括号和return都省了。3.3 、带多个参数根据输入的运算符计算两个数的运算,并返回结果:interface Model4{ String func(int a, int b, String oper);} Model4 md4 = (a, b, s) -> { String res = “”; if("+".equals(s)){ res = ( a+b ) + “”; }else if(”-".equals(s)){ res = ( a-b ) + “”; }else if(”".equals(s)){ res = ( ab ) + “”; }else if("/".equals(s)){ res = ( a/b ) + “”; // 暂不考虑除0的情况 }else{ res = “操作有失误”; } return res; }; System.out.println(md4.func(1,1,"+"));以上例子为多个参数的Lambda表达式,其中省略掉了每一个参数的类型,编译器自动推断。多条语句时实现体的{}不能省。4、Lambda作为参数在jdk8之前,接口可以作为方法参数传入,执行时必须提供接口实现类的实例。从java8开始,Lambda可以作为接口方法实现,当作参数传入,无论从形式上还是实际上都省去了对象的创建。使代码更加的紧凑简单高效。使用Lambda表达式需要有以下几步: 1、定义接口,抽象方法的模板; 2、在某方法中需要接口作为参数; 3、调用方法时需要将抽象方法实现(此时我们使用Lambda表达式)并传入即可。4.1、定义接口在接口中,必须有且仅有一个抽象方法,以确定Lambda模板// 无参无返回值的方法interface LambdaInterface1{ void printString();}// 带参无返回值的方法interface LambdaInterface2{ void printString(String str);}4.2、定义方法接收参数在某方法中需要使用接口作为参数// 无参public static void testLambda(LambdaInterface1 lam1){ lam1.printString();}// 带参public static void testLambda2(String s,LambdaInterface2 lam2){ lam2.printString(s);}4.3、Lambda实现使用方法时需要用Lambda将抽象方法实现使用方法时需要用Lambda将抽象方法实现// 无参Lambda作为参数testLambda(()->{ System.out.println(“可以简单,可以复杂”);});// 带参Lambda作为参数testLambdaParam(“hello”,(a)->{ System.out.println(a);});通过以上三步,能够完整地展示Lambda从和演变而来。此后在使用时,jdk中已经提供很多场景了,即前两部已经完成,我们更多的是实现第三步即可。5、forEach展示Lambda例如以ArrayList的遍历为例子,分析Lambda的使用方式。public static void main(String[] args) { List<String> strs = new ArrayList<String>(){ { add(“aaa”); add(“bbb”); add(“ccc”); } }; strs.forEach((str)-> System.out.println(str));}下面看看forEach的源码,定义中使用了接口Consumer作为参数,并调用了其方法:Consumer中的抽象方法只有accept一个通过在forEach方法中调用Consumer的accept方法,并将每一个元素作为参数传入,使得accept方法可以对每一个元素进行操作,当我们使用Lambda实现accept时就变成了我们自己对每一个元素的处理了。我们只负责处理即可。6、Lambda中使用变量在Lambda中可以定义自己的局部变量,也可以使用外层方法的局部变量,还可以使用属性。这一点也不难理解,既然是一个方法的实现,只写了一个代码块,那么使用本身所属方法的局部变量和类的属性也并不过分。public static void main(String[] args) { List<String> strs = new ArrayList<String>(){ { add(“aaa”); add(“bbb”); add(“ccc”); } }; int j = 1; strs.forEach((str)->{ int i = 0; System.out.println(str + " " + i + " " + j); });}注意:此时外部局部变量将自动变为final7、Lambda作为方法返回值例子:返回判断字符串是否为空public class Demo004_2 { public static void main(String[] args) { System.out.println(testLambda().isEmpty(“string”)); } // 判断字符串是否为空 public static AssertEmpty testLambda(){ return (n)-> null==n||n.trim().isEmpty(n); }}interface AssertEmpty{ boolean isEmpty(String str);}今天关于Java8新特性-Lambda表达式就讲到这里了,接下来我会继续讲述Java8新特性之函数式接口。敬请继续关注!欢迎留言评论。 ...

April 2, 2019 · 2 min · jiezi

【修炼内功】[Java8] Lambda表达式带来的编程新思路

该文章已收录 【修炼内功】跃迁之路Lambda表达式,可以理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型。这里,默认您已对Java8的Lambda表达式有一定了解,并且知道如何使用。Java8中引入的Lambda表达式,为编程体验及效率带来了极大的提升。行为参数化行为参数化,是理解函数式编程的一个重要概念。简单来说便是,一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。更为通俗的讲,行为参数化是指,定义一段代码,这段代码并不会立即执行,而是可以像普通变量/参数一样进行传递,被程序的其他部分调用。我们通过一个特别通用的筛选苹果的例子,来逐步了解如何使用Lambda表达式实现行为参数化。(如果对行为参数化已十分了解,可直接跳过本节)需求1:筛选绿色苹果我们需要将仓库中绿色的苹果过滤出来,对于这样的问题,大多数人来说都是手到擒来 (step1: 面向过程)public static List<Apple> filterGreenApples(List<Apple> apples) { List<apple> filteredApples = new LinkedList<>(); for (Apple apple: apples) { if (“green”.equals(apple.getColor())) { filteredApples.add(apple); } } return filteredApples;}List<Apple> greenApples = filterGreenApples(inventory);需求2:筛选任意颜色苹果对于这样的需求变更,可能也不是很难public static List<Apple> filterApplesByColor(List<Apple> apples, String color) { List<apple> filteredApples = new LinkedList<>(); for (Apple apple: apples) { if (color.equals(apple.getColor())) { filteredApples.add(apple); } } return filteredApples;}List<Apple> someColorApples = filterApplesByColor(inventory, “red”);需求3:筛选重量大于150克的苹果有了先前的教训,可能会学聪明一些,不会把重量直接写死到程序里,而是提供一个入参public static List<Apple> filterApplesByWeight(List<Apple> apples, int minWeight) { List<apple> filteredApples = new LinkedList<>(); for (Apple apple: apples) { if (apple.getWeight() > minWeight) { filteredApples.add(apple); } } return filteredApples;}List<Apple> heavyApples = filterApplesByColor(inventory, 150);需求4:筛选颜色为红色且重量大于150克的苹果如果照此下去,程序将变得异常难于维护,每一次小的需求变更,都需要进行大范围的改动。为了避免永无休止的加班,对于了解设计模式的同学,可能会将筛选逻辑抽象出来 (step2: 面向对象)public interface Predicate<Apple> { boolean test(Apple apple);}预先定义多种筛选策略,将策略动态的传递给筛选函数public static List<Apple> filterApples(List<Apple> apples, Predicate predicate) { List<apple> filteredApples = new LinkedList<>(); for (Apple apple: apples) { if (predicate.test(apple)) { filteredApples.add(apple); } } return filteredApples;}Predicate predicate = new Predicate() { @override public boolean test(Apple apple) { return “red”.equals(apple.getColor()) && apple.getWeight > 150; }};List<Apple> satisfactoryApples = filterApples(inventory, predicate);或者直接使用匿名类,将筛选逻辑传递给筛选函数List<Apple> satisfactoryApples = filterApples(inventory, new Predicate() { @override public boolean test(Apple apple) { return “red”.equals(apple.getColor()) && apple.getWeight > 150; }});至此,已经可以满足大部分的需求,但对于这种十分啰嗦、被Java程序员诟病了多年的语法,在Lambda表达式出现后,便出现了一丝转机 (step3: 面向函数)@FunctionalInterfacepublic interface Predicate<Apple> { boolean test(Apple apple);}public List<Apple> filterApples(List<Apple> apples, Predicate<Apple> predicate) { return apples.stream.filter(predicate::test).collect(Collectors.toList());}List<Apple> satisfactoryApples = filterApples(inventory, apple -> “red”.equals(apple.getColor()) && apple.getWeight > 150);以上示例中使用了Java8的stream及lambda表达式,关于stream及lambda表达式的具体使用方法,这里不再赘述,重点在于解释什么是行为参数化,示例中直接将筛选逻辑(红色且重量大于150克)的代码片段作为参数传递给了函数(确切的说是将lambda表达式作为参数传递给了函数),而这段代码片段会交由筛选函数进行执行。Lambda表达式与匿名类很像,但本质不同,关于Lambda表达式及匿名类的区别,会在之后的文章详细介绍如果想让代码更为简洁明了,可以继续将筛选逻辑提取为函数,使用方法引用进行参数传递private boolean isRedColorAndWeightGt150(Apple apple) { return “red”.equals(apple.getColor()) && apple.getWeight > 150;}List<Apple> satisfactoryApples = filterApples(inventory, this::isRedColorAndWeightGt150);至此,我们完成了从面向过程到面向对象再到面向函数的编程思维转变,代码也更加具有语义化,不论是代码阅读还是维护,都较之前有了很大的提升等等,如果需要过滤颜色为黄色并且重量在180克以上的苹果,是不是还要定义一个isYellowColorAndWeightGt180的函数出来,貌似又陷入了无穷加班的怪圈~还有没有优化空间?能否将筛选条件抽离到单一属性,如byColor、byMinWeight等,之后再做与或计算传递给筛选函数?接下来就是我们要介绍的高阶函数高阶函数高阶函数是一个函数,它接收函数作为参数或将函数作为输出返回接受至少一个函数作为参数返回的结果是一个函数以上的定义听起来可能有些绕口。结合上节示例,我们的诉求是将苹果的颜色、重量或者其他筛选条件也抽离出来,而不是硬编码到代码中private Predicate<apple> byColor(String color) { return (apple) -> color.equals(apple.getColor);}private Predicate<Apple> byMinWeight(int minWeight) { return (apple) -> apple.getWeight > minWeight;}以上两个函数的返回值,均为Predicate类型的Lambda表达式,或者可以说,以上两个函数的返回值也是函数接下来我们定义与运算,只有传入的所有条件均满足才算最终满足private Predicate<Apple> allMatches(Predicate<Apple> …predicates) { return (apple) -> predicates.stream.allMatch(predicate -> predicate.test(apple));}以上函数,是将多个筛选逻辑做与计算,注意,该函数接收多个函数(Lambda)作为入参,并返回一个函数(Lambda),这便是高阶函数如何使用该函数?作为苹果筛选示例的延伸,我们可以将上一节最后一个示例代码优化如下List<Apple> satisfactoryApples = filterApples(inventory, allMatches(byColor(“red”), byMinWeight(150)));至此,还可以抽象出anyMatches、nonMatches等高阶函数,组合使用// 筛选出 颜色为红色 并且 重量在150克以上 并且 采摘时间在1周以内 并且 产地在中国、美国、加拿大任意之一的苹果List<Apple> satisfactoryApples = filterApples( inventory, allMatches( byColor(“red”), byMinWeight(150), apple -> apple.pluckingTime - currentTimeMillis() < 7L * 24 * 3600 * 1000, anyMatches(byGardens(“中国”), byGardens(“美国”), byGardens(“加拿大”) ));如果使用jvm包中的java.util.function.Predicate,我们还可以继续优化,使代码更为语义化// 筛选出 颜色为红色 并且 重量在150克以上 并且 采摘时间在1周以内 并且 产地在中国、美国、加拿大任意之一的苹果List<Apple> satisfactoryApples = filterApples( inventory, byColor(“red”) .and(byMinWeight(150)) .and(apple -> apple.pluckingTime - currentTimeMillis() < 7L * 24 * 3600 * 1000) .and(byGardens(“中国”).or(byGardens(“美国”).or(byGardens(“加拿大”))));这里使用了Java8中的默认函数,默认函数允许你在接口interface中定义函数的默认行为,从某方面来讲也可以实现类的多继承示例中,and/or函数接收一个Predicate函数(Lambda表达式)作为参数,并返回一个Predicate函数(Lambda表达式),同样为高阶函数关于默认函数的使用,会在之后的文章详细介绍闭包闭包(Closure),能够读取其他函数内部变量的函数又是一个比较抽象的概念,其实在使用Lambda表达式的过程中,经常会使用到闭包,比如public Future<List<Apple>> filterApplesAsync() { List<Apple> inventory = getInventory(); return CompletableFuture.supplyAsync(() -> filterApples(inventory, byColor(“red”)));}在提交异步任务时,传入了内部函数(Lambda表达式),在内部函数中使用了父函数filterApplesAsync中的局部变量inventory,这便是闭包。如果该示例不够明显的话,可以参考如下示例private Supplier<Integer> initIntIncreaser(int i) { AtomicInteger atomicInteger = new AtomicInteger(i); return () -> atomicInteger.getAndIncrement();}Supplier<Integer> increaser = initIntIncreaser(1);System.out.println(increaser.get());System.out.println(increaser.get());System.out.println(increaser.get());System.out.println(increaser.get());//[out]: 1//[out]: 2//[out]: 3//[out]: 4initIntIncreaser函数返回另一个函数(内部函数),该函数(increaser)使用了父函数initIntIncreaser的局部变量atomicInteger,该变量会被函数increaser持有,并且会在调用increaser时使用(更改)柯里化柯里化(Currying),是把接受多个参数的函数变换成接受一个单一参数的函数。柯里化是逐步传值,逐步缩小函数的适用范围,逐步求解的过程。如,设计一个函数,实现在延迟一定时间之后执行给定逻辑,并可以指定执行的执行器public ScheduledFuture executeDelay(Runnable runnable, ScheduledExecutorService scheduler, long delay, TimeUnit timeunit) { return scheduler.schedule(runnable, delay, timeunit);}目前有一批任务,需要使用执行器scheduler1,并且均延迟5分钟执行另一批任务,需要使用执行器scheduler2,并且均延迟15分钟执行可以这样实现executeDelay(runnable11, scheduler1, 5, TimeUnit.SECONDS);executeDelay(runnable12, scheduler1, 5, TimeUnit.SECONDS);executeDelay(runnable13, scheduler1, 5, TimeUnit.SECONDS);executeDelay(runnable21, scheduler2, 15, TimeUnit.SECONDS);executeDelay(runnable22, scheduler2, 15, TimeUnit.SECONDS);executeDelay(runnable23, scheduler2, 15, TimeUnit.SECONDS);其实,我们发现这里是有规律可循的,比如,使用某个执行器在多久之后执行什么,我们可以将executeDelay函数进行第一次柯里化public Function3<ScheduledFuture, Runnable, Integer, TimeUnit> executeDelayBySomeScheduler(ScheduledExecutorService scheduler) { return (runnable, delay, timeunit) -> executeDelay(runable, scheduler, delay, timeunit);}Function3<ScheduledFuture, Runnable, Integer, TimeUnit> executeWithScheduler1 = executeDelayBySomeScheduler(scheduler1);Function3<ScheduledFuture, Runnable, Integer, TimeUnit> executeWithScheduler2 = executeDelayBySomeScheduler(scheduler2);executeWithScheduler1.apply(runnable11, 5, TimeUnit.SECONDS);executeWithScheduler1.apply(runnable12, 5, TimeUnit.SECONDS);executeWithScheduler1.apply(runnable13, 5, TimeUnit.SECONDS);executeWithScheduler2.apply(runnable21, 15, TimeUnit.SECONDS);executeWithScheduler2.apply(runnable22, 15, TimeUnit.SECONDS);executeWithScheduler2.apply(runnable23, 15, TimeUnit.SECONDS);函数executeDelay接收4个参数,函数executeWithScheduler1/executeWithScheduler2接收3个参数,我们通过executeDelayBySomeScheduler将具体的执行器封装在了executeWithScheduler1/executeWithScheduler2中进一步,我们可以做第二次柯里化,将延迟时间也封装起来public Function<ScheduledFuture, Runnable> executeDelayBySomeSchedulerOnDelay(ScheduledExecutorService scheduler, long delay, TimeUnit timeunit) { return (runnable) -> executeDelay(runable, scheduler, delay, timeunit);}Function<ScheduledFuture, Runnable> executeWithScheduler1After5M = executeDelayBySomeSchedulerOnDelay(scheduler1, 5, TimeUnit.SECONDS);Function<ScheduledFuture, Runnable> executeWithScheduler2After15M = executeDelayBySomeSchedulerOnDelay(scheduler2, 15, TimeUnit.SECONDS);Stream.of(runnable11, runnable12,runnable13).forEach(this::executeWithScheduler1After5M);Stream.of(runnable21, runnable22,runnable23).forEach(this::executeWithScheduler2After15M);将具体的执行器及延迟时间封装在executeWithScheduler1After5M/executeWithScheduler2After15M中,调用的时候,只需要关心具体的执行逻辑即可环绕执行(提取共性)有时候我们会发现,很多代码块十分相似,但又有些许不同比如,目前有两个接口可以查询汇率,queryExchangeRateA及queryExchangeRateB,我们需要在开关exchangeRateSwitch打开的时候使用queryExchangeRateA查询,否则使用queryExchangeRateB查询,同时在一个接口异常失败的时候,自动降低到另一个接口进行查询同样,目前有两个接口可以查询关税,queryTariffsA及queryTariffsB,同样地,我们需要在开关tariffsSwitch打开的时候使用queryTariffsA查询,否则使用queryTariffsB查询,同时在一个接口异常失败的时候,自动降低到另一个接口进行查询其实,以上两种场景,除了开关及具体的接口逻辑外,整体流程是一致的再分析,其实接口调用的降级逻辑也是一样的这里不再列举如何使用抽象类的方法如解决该类问题,我们直接使用Java8的Lambda表达式首先,可以将降级逻辑提取为一个函数@FunctionalInterfaceinterface ThrowingSupplier<T> { T get() throw Exception;}/** * 1. 执行A * 2. 如果A执行异常,则执行B /public <T> ThrowingSupplier<T> executeIfThrowing(ThrowingSupplier<T> supplierA, ThrowingSupplier<T> supplierB) throws Exception { try { return supplierA.get(); } catch(Exception e) { // dill with exception return supplierB.get(); }}至此,我们完成了降级的逻辑。接来下,将开关逻辑提取为一个函数/* * 1. switcher打开,执行A * 2. switcher关闭,执行B /public <T> T invoke(Supplier<Boolean> switcher, ThrowingSupplier<T> executeA, ThrowingSupplier<T> executeB) throws Exception { return switcher.get() ? executeIfThrowing(this::queryExchangeRateA, this::queryExchangeRateB) : executeIfThrowing(this::queryExchangeRateB, this::queryExchangeRateA);}回到上边的两个需求,查询汇率及关税,我们可以/* * 查询汇率 /val exchangeRate = invoke( exchangeRateSwitch::isOpen, this::queryExchangeRateA, this::queryExchangeRateB)/* * 查询关税 */val queryTariffs = invoke( tariffsSwitch::isOpen, this::queryTariffsA, this::queryTariffsB)以上,用到了ThrowingSupplier,该点会在《 [Java] Lambda表达式“陷阱”》中详细介绍设计模式Lambda表达式,会给以往面向对象思想的设计模式带来全新的设计思路,这部分内容希望在设计模式系列文章中详细介绍。关于Lambda表达式,还有非常多的内容及技巧,无法使用有限的篇幅进行介绍,同时也希望与各位一同讨论。 ...

April 1, 2019 · 3 min · jiezi

Lambda表达式与Stream流 (终)

package com.java.design.java8;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.time.;import java.time.format.DateTimeFormatter;import java.time.temporal.ChronoField;import java.util.;import java.util.concurrent.CompletableFuture;import java.util.stream.Collectors;import java.util.stream.IntStream;/** * @author 陈杨 * / @SpringBootTest@RunWith(SpringRunner.class)public class LambdaInfo {一、Lambda表达式与Stream流/ A lambda expression can be understood as a concise representation of an anonymous function that can be passed around: it doesn’t have a name, but it has a list of parameters, a body, a return type, and also possibly a list of exceptions that can be thrown. That’s one big definition; let’s break it down: Anonymous: We say anonymous because it doesn’t have an explicit name like a method would normally have: less to write and think about! Function: We say function because a lambda isn’t associated with a particular class like a method is. But like a method, a lambda has a list of parameters, a body, a return type, and a possible list of exceptions that can be thrown. Passed around: A lambda expression can be passed as argument to a method or stored in a variable. Concise: You don’t need to write a lot of boilerplate like you do for anonymous classes.// Stream : A sequence of elements from a source that supports data processing operations. Sequence of elements Source Pipelining Internal iteration Traversable only once Collections: external interation using an interator behind the scenes*/二、初始化测试数据private List<Integer> list;@Beforepublic void init() { list = IntStream.rangeClosed(1, 100).boxed().collect(Collectors.toList()); list.sort(Collections.reverseOrder());}三、各种API1.allMatch@Testpublic void testLambdaInfo() { System.out.println(">———————Match方法———————-<"); // 一、Match方法 // Returns whether all elements of this stream match the provided predicate. Optional.of(list.stream().mapToInt(Integer::intValue).allMatch(i -> i > 0)) .ifPresent(System.out::println); // Returns whether any elements of this stream match the provided predicate. Optional.of(list.stream().mapToInt(Integer::intValue).anyMatch(i -> i > 0)) .ifPresent(System.out::println); // Returns whether no elements of this stream match the provided predicate.. Optional.of(list.stream().mapToInt(Integer::intValue).noneMatch(i -> i > 0)) .ifPresent(System.out::println);2、findSystem.out.println(">——————–Find方法———————–<");// 二、Find方法// Returns an Optional describing the first element of this stream,// or an empty Optional if the stream is empty.// If the stream has no encounter order, then any element may be returned.list.stream().mapToInt(Integer::intValue).filter(i -> i > 10).findFirst() .ifPresent(System.out::println);// Returns an Optional describing some element of the stream, or an empty Optional if the stream is empty.list.stream().mapToInt(Integer::intValue).filter(i -> i > 10).findAny() .ifPresent(System.out::println);3、reduceSystem.out.println(">———————Reduce方法———————-<");// 三、Reduce方法// Performs a reduction on the elements of this stream, using the provided identity value// and an associative accumulation function, and returns the reduced value.// 求和System.out.println(list.stream().reduce(0, Integer::sum));list.stream().mapToInt(Integer::intValue).reduce(Integer::sum) .ifPresent(System.out::println);// 求最大值System.out.println(list.stream().reduce(0, Integer::max));list.stream().mapToInt(Integer::intValue).reduce(Integer::max) .ifPresent(System.out::println);// 求最小值System.out.println(list.stream().reduce(0, Integer::min));list.stream().mapToInt(Integer::intValue).reduce(Integer::min) .ifPresent(System.out::println); System.out.println(">——————————————-<"); }4、CompletableFuture API@Testpublic void testCompletableFuture() { // 四、CompletableFuture API /* * Returns a new CompletableFuture that is asynchronously completed by a task * running in the given executor with the value obtained by calling the given Supplier. */ CompletableFuture.supplyAsync(list.stream().mapToInt(Integer::intValue)::sum, System.out::println); Optional.of(CompletableFuture.supplyAsync(list.stream().mapToInt(Integer::intValue)::sum) .complete(55)).ifPresent(System.out::println); // thenAccept 无返回值 Consumer<? super T> action CompletableFuture.supplyAsync(list.stream().mapToInt(Integer::intValue)::sum) .thenAccept(System.out::println); // thenApply 有返回值 Function<? super T,? extends U> fn CompletableFuture.supplyAsync(() -> list.stream().mapToInt(Integer::intValue)) .thenApply(IntStream::sum).thenAccept(System.out::println); // 对元素及异常进行处理 BiFunction<? super T, Throwable, ? extends U> fn CompletableFuture.supplyAsync(() -> list.stream().mapToInt(Integer::intValue)) .handle((i, throwable) -> “handle:\t” + i.sum()).thenAccept(System.out::println); // whenCompleteAsync 完成时执行 BiConsumer<? super T, ? super Throwable> action CompletableFuture.supplyAsync(list.stream().mapToInt(Integer::intValue)::sum) .whenCompleteAsync((value, throwable) -> System.out.println(“whenCompleteAsync:\t” + value)); // 组合CompletableFuture 将前一个结果作为后一个输入参数 (参照 组合设计模式) CompletableFuture.supplyAsync(() -> list.stream().mapToInt(Integer::intValue)) .thenCompose(i -> CompletableFuture.supplyAsync(i::sum)).thenAccept(System.out::println); // 合并CompletableFuture CompletableFuture.supplyAsync(list.stream().mapToInt(Integer::intValue)::sum) .thenCombine(CompletableFuture.supplyAsync(() -> list.stream() .mapToDouble(Double::valueOf).sum()), Double::sum).thenAccept(System.out::println); // 合并CompletableFuture CompletableFuture.supplyAsync(list.stream().mapToInt(Integer::intValue)::sum) .thenAcceptBoth(CompletableFuture.supplyAsync(list.stream() .mapToDouble(Double::valueOf)::sum), (r1, r2) -> System.out.println(“thenAcceptBoth:\t” + r1 + “\t” + r2)); // 2个CompletableFuture运行完毕后运行Runnable CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + “\tis running”); return list.stream().mapToInt(Integer::intValue).sum(); }) .runAfterBoth( CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + “\tis running”); return list.stream().mapToDouble(Double::valueOf).sum(); }), () -> System.out.println(“The 2 method have done”)); // 2个CompletableFuture 有一个运行完就执行Runnable CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + “\tis running”); return list.stream().mapToInt(Integer::intValue).sum(); }) .runAfterEither( CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + “\tis running”); return list.stream().mapToDouble(Double::valueOf).sum(); }), () -> System.out.println(“The 2 method have done”)); // 2个CompletableFuture 有一个运行完就执行Function<? super T, U> fn CompletableFuture.supplyAsync( list.stream().mapToInt(Integer::intValue).max()::getAsInt) .applyToEither( CompletableFuture.supplyAsync(list.stream().mapToInt(Integer::intValue).min()::getAsInt) , v -> v * 10) .thenAccept(System.out::println); // 2个CompletableFuture 有一个运行完就执行Consumer<? super T> action CompletableFuture.supplyAsync( list.stream().mapToInt(Integer::intValue).max()::getAsInt) .acceptEither( CompletableFuture.supplyAsync(list.stream().mapToInt(Integer::intValue).min()::getAsInt) , System.out::println); // 将集合中每一个元素都映射成为CompletableFuture<Integer>对象 List<CompletableFuture<Integer>> collect = list.stream().map(i -> CompletableFuture.supplyAsync(i::intValue)) .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); // 集合转数组 CompletableFuture[] completableFutures = collect.toArray(CompletableFuture[]::new); // 有一个task执行完毕 CompletableFuture.anyOf(completableFutures) .thenRun(() -> System.out.println(“有一个task执行完毕—>first done”)); // 有且仅有所有task执行完毕 CompletableFuture.allOf(completableFutures) .thenRun(() -> System.out.println(“有且仅有所有task执行完毕—>done”));}5、Java.time API @Test public void testLocalDateTime() { // 五、Java.time API LocalDate localDate = LocalDate.of(2019, 12, 1); // 当前时间 Optional.of(LocalDate.now()).ifPresent(System.out::println); // 年份 Optional.of(localDate.getYear()).ifPresent(System.out::println); OptionalInt.of(localDate.get(ChronoField.YEAR)).ifPresent(System.out::println); // 月份 (Jan–>Dec) Optional.of(localDate.getMonth()).ifPresent(System.out::println); // 月份(1–>12) Optional.of(localDate.getMonthValue()).ifPresent(System.out::println); OptionalInt.of(localDate.get(ChronoField.MONTH_OF_YEAR)).ifPresent(System.out::println); // 年中的第几天 Optional.of(localDate.getDayOfYear()).ifPresent(System.out::println); OptionalInt.of(localDate.get(ChronoField.DAY_OF_YEAR)).ifPresent(System.out::println); // 月中的第几天 Optional.of(localDate.getDayOfMonth()).ifPresent(System.out::println); OptionalInt.of(localDate.get(ChronoField.DAY_OF_MONTH)).ifPresent(System.out::println); // 星期几(Mon–>Sun) Optional.of(localDate.getDayOfWeek()).ifPresent(System.out::println); // 星期几(1–>7) OptionalInt.of(localDate.get(ChronoField.DAY_OF_WEEK)).ifPresent(System.out::println); // 时代(公元前、后) CE BCE Optional.of(localDate.getEra()).ifPresent(System.out::println); // 时代(公元前、后) 1—>CE 0—>BCE Optional.of(localDate.getEra().getValue()).ifPresent(System.out::println); OptionalInt.of(localDate.get(ChronoField.ERA)).ifPresent(System.out::println); // ISO年表 Optional.of(localDate.getChronology().getId()).ifPresent(System.out::println); // 当前时间 LocalTime time = LocalTime.now(); // 时 OptionalInt.of(time.getHour()).ifPresent(System.out::println); OptionalInt.of(time.get(ChronoField.HOUR_OF_DAY)).ifPresent(System.out::println); // 分 OptionalInt.of(time.getMinute()).ifPresent(System.out::println); OptionalInt.of(time.get(ChronoField.MINUTE_OF_DAY)).ifPresent(System.out::println); // 秒 OptionalInt.of(time.getSecond()).ifPresent(System.out::println); OptionalInt.of(time.get(ChronoField.SECOND_OF_DAY)).ifPresent(System.out::println); // 纳秒 OptionalInt.of(time.getNano()).ifPresent(System.out::println); OptionalLong.of(time.getLong(ChronoField.NANO_OF_SECOND)).ifPresent(System.out::println); // 中午时间 Optional.of(LocalTime.NOON).ifPresent(System.out::println); // 午夜时间 Optional.of(LocalTime.MIDNIGHT).ifPresent(System.out::println); // 自定义格式化时间 DateTimeFormatter customDateTimeFormatter = DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm:ss E”); LocalDateTime localDateTime = LocalDateTime.of(localDate, time); Optional.of(localDateTime.format(customDateTimeFormatter)).ifPresent(System.out::println); // 根据传入的文本匹配自定义指定格式进行解析 Optional.of(LocalDateTime.parse(“2019-12-25 12:30:00 周三”, customDateTimeFormatter)) .ifPresent(System.out::println); // 时间点 Instant Instant start = Instant.now(); try { Thread.sleep(10_000); } catch (InterruptedException e) { e.printStackTrace(); } Instant end = Instant.now(); // Duration 时间段 Duration duration = Duration.between(start, end); OptionalLong.of(duration.toNanos()).ifPresent(System.out::println); // Period 时间段 Period period = Period.between(LocalDate.now(), localDate); OptionalInt.of(period.getYears()).ifPresent(System.out::println); OptionalInt.of(period.getMonths()).ifPresent(System.out::println); OptionalInt.of(period.getDays()).ifPresent(System.out::println); // The Difference Between Duration And Period // Durations and periods differ in their treatment of daylight savings time when added to ZonedDateTime. // A Duration will add an exact number of seconds, thus a duration of one day is always exactly 24 hours. // By contrast, a Period will add a conceptual day, trying to maintain the local time. }} ...

March 11, 2019 · 4 min · jiezi

Stream流与Lambda表达式(五) Stream BaseStream AutoCloseable源码解读

package com.java.design.java8.Stream.StreamDetail;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.Arrays;import java.util.List;import java.util.UUID;import java.util.stream.Stream;/** * @author 陈杨 /@SpringBootTest@RunWith(SpringRunner.class)public class StreamDetail { private List<Integer> integerList; @Before public void init() { integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0); } @Test public void testStreamDetail() {一、流的定义 // Stream : A sequence of elements supporting sequential and parallel aggregate operations. // 流: 支持 串行、并行 聚合操作 元素序列二、流的创建 // 流的创建: // Collection.stream() Collection.parallelStream() // Stream.generate System.out.println("—————————————–\n"); System.out.println(“以Stream.generate方法生成流”); Stream<String> generate = Stream.generate(UUID.randomUUID()::toString); generate.findFirst().ifPresent(System.out::println); System.out.println("—————————————–\n");三、对象引用流 // 对象引用流 // Stream , which is a stream of object references四、流的计算 // 流的计算: stream pipeline // To perform a computation, stream operations are composed into a stream pipeline.五、流管道组成 // 流管道组成: 源数据source–>数组、集合、生成器函数、IO通道等 // [0,n) 中间操作(intermediate operations) 一个流转换 为 另一个新流 // 终止操作(terminal operation)产生一个结果 或有副作用(修改流中元素 属性或状态) // A stream pipeline consists of a source (which might be an array, // a collection, a generator function, an I/O channel,etc), // zero or more <em>intermediate operations</em> (which transform a // stream into another stream, such as {@link Stream#filter(Predicate)}), and a // <em>terminal operation</em> (which produces a result or side-effect, such // as {@link Stream#count()} or {@link Stream#forEach(Consumer)}).六、流的消费 // 流的消费: 流中对于源数据的计算 有且仅有在终止操作触发时才会被调用 // 流中元素只有在被需要时才会被消费 // lazy(惰性): 如果没有终止操作 那么一系列的中间操作都不会被执行 // stream流操作只会执行一次: stream中有一个容器 将所有中间操作打包 放入容器中 // 调用终止操作时 触发容器的链式中间操作 将流中每一个元素 应用于中间业务逻辑 // Streams are lazy; computation on the source data is only performed when the // terminal operation is initiated, and source elements are consumed only as needed. // 流创建后 只能被消费一次 否则抛异常 // 除非流被设计成为显示并发修改的流如ConcurrentHashMap 否则未期望或错误的行为就会在执行时产生 // Unless the source was explicitly designed for concurrent modification // (such as a ConcurrentHashMap),unpredictable or erroneous behavior may result // from modifying the stream source while it is being queried. // java.lang.IllegalStateException: stream has already been operated upon or closed七、 Lambda表达式的正确行为 // Lambda表达式的正确行为: // To preserve correct behavior,these <em>behavioral parameters</em>: // must be <a href=“package-summary.html#NonInterference”>non-interfering</a> // (they do not modify the stream source); // in most cases must be <a href=“package-summary.html#Statelessness”>stateless</a> // (their result should not depend on any state that might change during execution // of the stream pipeline).八、流与集合// 流与集合:// 集合关注的是对元素的管理与访问// 流不会直接提供直接访问或操作其元素的方法// 流提供声明性描述: 源 与 建立于源之上的聚合计算执行操作// 如果流没有提供预期的功能 可执行受控遍历(iterator、spliterator)// Collections and streams, while bearing some superficial similarities,// have different goals. Collections are primarily concerned with the efficient// management of, and access to, their elements. By contrast, streams do not// provide a means to directly access or manipulate their elements, and are// instead concerned with declaratively describing their source and the// computational operations which will be performed in aggregate on that source.// However, if the provided stream operations do not offer the desired// functionality, the {@link #iterator()} and {@link #spliterator()} operations// can be used to perform a controlled traversal.九、 流的MapReduce操作// 流的MapReduce操作 求集合 每个元素的2倍 之和// 此例中:Integer 每执行一次reduce操作 触发 该元素的map操作一次System.out.println(integerList.stream().map(i -> 2 * i).reduce(0, Integer::sum));System.out.println("—————————————–\n");十、流资源自动关闭 AutoCloseable接口实现package com.java.design.java8.Stream.StreamDetail;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;/* * @author 陈杨 /@SpringBootTest@RunWith(SpringRunner.class)public class AutoCloseableTest implements AutoCloseable { / * An object that may hold resources (such as file or socket handles) * until it is closed. The {@link #close()} method of an {@code AutoCloseable} * object is called automatically when exiting a {@code * try}-with-resources block for which the object has been declared in * the resource specification header. This construction ensures prompt * release, avoiding resource exhaustion exceptions and errors that * may otherwise occur. * * 实现AutoCloseable接口: * 使用try–with–resources 代码块 替代 try–catch–finally * 在代码块运行完毕后 自动实现 资源的关闭 * public interface AutoCloseable { * void close() throws Exception; * } / public void doSomeThing() { System.out.println(“method doSomeThing invoked!”); } @Override public void close() throws Exception { System.out.println(“method close invoked!”); } @Test public void testAutoCloseable() throws Exception { try (AutoCloseableTest autoCloseableTest = new AutoCloseableTest()) { autoCloseableTest.doSomeThing(); } }}十一、认识BaseStream 与 closeHandlerpackage com.java.design.java8.Stream.StreamDetail.BaseStreamDetail;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.Arrays;import java.util.List;import java.util.stream.Stream;/* * @author 陈杨 */@SpringBootTest@RunWith(SpringRunner.class)public class BaseStreamDetail { private List<String> list; private NullPointerException myException; @Before public void init() { list = Arrays.asList(“Kirito”, “Asuna”, “Illyasviel”, “Sakura”); myException = new NullPointerException(“my NullPointerException”); } @Test public void testBaseStreamDetail() { // public interface Stream<T> extends BaseStream<T, Stream<T>> /public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable { Iterator<T> iterator(); * Returns a spliterator for the elements of this stream. Spliterator<T> spliterator(); * 当终止操作应用于流时 判断其是否并行 * 当执行终止操作之后 执行isParallel() 会得到不可预期的结果 * 此方法需适用于终止操作之前 * * Returns whether this stream, if a terminal operation were to be executed, * would execute in parallel. Calling this method after invoking an * terminal stream operation method may yield unpredictable results. * boolean isParallel(); * 返回一个等价的串行流 * 流本身已经是串行 或 流的状态已被修改为串行 * * Returns an equivalent stream that is sequential. May return * itself, either because the stream was already sequential, or because * the underlying stream state was modified to be sequential. * S sequential(); * 返回一个等价的并行流 * 流本身已经是并行 或 流的状态已被修改为并行 * * Returns an equivalent stream that is parallel. May return * itself, either because the stream was already parallel, or because * the underlying stream state was modified to be parallel. * S parallel(); * 返回一个等价的无序流 * 流本身已经是无序 或 流的状态已被修改为无序 * * Returns an equivalent stream that is * <a href=“package-summary.html#Ordering”>unordered</a>. May return * itself, either because the stream was already unordered, or because * the underlying stream state was modified to be unordered. * S unordered(); * 返回一个等价的流 有close handler * close handler当流close()方法调用时触发 * 调用顺序:close handlers被添加先后顺序 * * 所有的close handlers都会被调用 即使出现了异常 * 如果任意close handler抛出异常 * 那么第一个异常会传递给调用段 * 其他异常(剩余或被抑制)会传递给调用段 * 除非其中有与第一个异常相同的异常(相同对象) 因为相同异常不能抑制自身 * * Returns an equivalent stream with an additional close handler. Close * handlers are run when the {@link #close()} method * is called on the stream, and are executed in the order they were * added. All close handlers are run, even if earlier close handlers throw * exceptions. If any close handler throws an exception, the first * exception thrown will be relayed to the caller of {@code close()}, with * any remaining exceptions added to that exception as suppressed exceptions * (unless one of the remaining exceptions is the same exception as the * first exception, since an exception cannot suppress itself.) May * return itself. * * @param closeHandler A task to execute when the stream is closed * @return a stream with a handler that is run if the stream is closed S onClose(Runnable closeHandler); * 关闭流 调用流管道中的close handlers * * Closes this stream, causing all close handlers for this stream pipeline to be called. @Override void close(); }/ try (Stream<String> stream = list.stream()) { stream.onClose(() -> { System.out.println(“close handler first”); // throw new NullPointerException(“null pointer exception 1”); throw myException; }).onClose(() -> { System.out.println(“close handler second”); // throw new NullPointerException(“null pointer exception 2”); throw myException; }).onClose(() -> { System.out.println(“close handler third”); // throw new NullPointerException(“null pointer exception 3”); throw myException; }).forEach(System.out::println); } }}十二、测试结果testStreamDetail测试 . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 17:40:31.060 INFO 15872 --- [ main] c.j.d.j.S.StreamDetail.StreamDetail : Starting StreamDetail on DESKTOP-87RMBG4 with PID 15872 (started by 46250 in E:\IdeaProjects\design)2019-02-20 17:40:31.062 INFO 15872 --- [ main] c.j.d.j.S.StreamDetail.StreamDetail : No active profile set, falling back to default profiles: default2019-02-20 17:40:31.584 INFO 15872 --- [ main] c.j.d.j.S.StreamDetail.StreamDetail : Started StreamDetail in 0.728 seconds (JVM running for 1.461)-----------------------------------------以Stream.generate方法生成流2742c0e9-7bdb-4c7e-88c9-b5d94684215c-----------------------------------------90-----------------------------------------AutoCloseableTest测试 . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _ | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 17:39:45.456 INFO 16320 — [ main] c.j.d.j.S.S.AutoCloseableTest : Starting AutoCloseableTest on DESKTOP-87RMBG4 with PID 16320 (started by 46250 in E:\IdeaProjects\design)2019-02-20 17:39:45.457 INFO 16320 — [ main] c.j.d.j.S.S.AutoCloseableTest : No active profile set, falling back to default profiles: default2019-02-20 17:39:45.956 INFO 16320 — [ main] c.j.d.j.S.S.AutoCloseableTest : Started AutoCloseableTest in 0.716 seconds (JVM running for 1.433)method doSomeThing invoked!method close invoked!testBaseStreamDetail测试 . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / ` | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 17:46:41.886 INFO 15216 — [ main] c.j.d.j.S.S.B.BaseStreamDetail : Starting BaseStreamDetail on DESKTOP-87RMBG4 with PID 15216 (started by 46250 in E:\IdeaProjects\design)2019-02-20 17:46:41.887 INFO 15216 — [ main] c.j.d.j.S.S.B.BaseStreamDetail : No active profile set, falling back to default profiles: default2019-02-20 17:46:42.435 INFO 15216 — [ main] c.j.d.j.S.S.B.BaseStreamDetail : Started BaseStreamDetail in 0.762 seconds (JVM running for 1.48)KiritoAsunaIllyasvielSakuraclose handler firstclose handler secondclose handler thirdjava.lang.NullPointerException: my NullPointerExceptionProcess finished with exit code -1 ...

February 20, 2019 · 7 min · jiezi

Stream流与Lambda表达式(六) SpliteratorDetail

package com.java.design.java8.Stream.StreamDetail.BaseStreamDetail;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.Arrays;import java.util.List;import java.util.function.Consumer;import java.util.function.IntConsumer;/** * @author 陈杨 /@SpringBootTest@RunWith(SpringRunner.class)public class SpliteratorDetail { private IntConsumer intConsumer; private Consumer consumer; private List<String> list; @Before public void init() { intConsumer = System.out::println; consumer = System.out::println; list = Arrays.asList(“Kirito”, “Asuna”, “Sinon”, “Yuuki”, “Alice”); } private void action(IntConsumer intConsumer) { intConsumer.accept(100); } @Test public void testSpliteratorDetail() {一、流的创建–源(集合)// 一、流的创建–源(集合)/Collection集合默认方法 list.stream()defaultStream<E> stream () { return StreamSupport.stream(spliterator(), false);}@OverridedefaultSpliterator<E> spliterator () { return Spliterators.spliterator(this, 0);}public static <T> Spliterator<T> spliterator(Collection<? extends T> c,int characteristics) { return new IteratorSpliterator<>(Objects.requireNonNull(c), characteristics);}/// Collector 接口 与 Collectors 静态类实现// Spliterator 接口 与 Spliterators 静态类实现二、Spliterator 接口// 二、Spliterator 接口// Spliterator 接口// 对数据源中元素进行遍历或分区// An object for traversing and partitioning elements of a source.// 延迟绑定数据源// 绑定时机:首次遍历、切分、查询大小 而不是在创建时// A <em>late-binding</em> Spliterator binds to the source of elements at the// point of first traversal, first split, or first query for estimated size,// rather than at the time the Spliterator is created.// 非延迟绑定数据源// 绑定时机:Spliterator创建时 或Spliterator的方法首次调用// A Spliterator that is not <em>late-binding</em> binds to the source of elements// at the point of construction or first invocation of any method.// Spliterator 与 Iterator 的区别://// Spliterator 优势:通过分解和单元素迭代 支持串行与并行// 比Iterator迭代通过hasNext与next性能更好// Spliterators, like {@code Iterator}s, are for traversing the elements of a source.// The Spliterator API was designed to support efficient parallel traversal// in addition to sequential traversal, by supporting decomposition as well as single-element iteration.// In addition, the protocol for accessing elements via a Spliterator is designed to impose// smaller per-element overhead than {@code Iterator}, and to avoid the inherent// race involved in having separate methods for {@code hasNext()} and {@code next()}.三、Spliterator特性值/ public interface Spliterator<T> {// 三、Spliterator特性值 * Characteristic value signifying that an encounter order is defined for * elements. If so, this Spliterator guarantees that method * {@link #trySplit} splits a strict prefix of elements, that method 分割前后对元素加严格前缀 * {@link #tryAdvance} steps by one element in prefix order, and that 按照元素的顺序前缀遍历 * {@link #forEachRemaining} performs actions in encounter order. 对剩余元素按照相遇顺序执行action * * <p>A {@link Collection} has an encounter order if the corresponding * {@link Collection#iterator} documents an order. If so, the encounter * order is the same as the documented order. Otherwise, a collection does * not have an encounter order. * 集合是有序的,则文档是有序的 * 集合是无序的,则文档是无序的 * * @apiNote Encounter order is guaranteed to be ascending index order for * any {@link List}. But no order is guaranteed for hash-based collections * such as {@link HashSet}. Clients of a Spliterator that reports * {@code ORDERED} are expected to preserve ordering constraints in * non-commutative parallel computations. * 基于索引升序的List 排序–>有序 * 基于Hash散列的HashSet 排序–>无序 * 非并发情况下期望要保留 有序集合中 元素的顺序 以返还给客户端调用者 public static final int ORDERED = 0x00000010; * Characteristic value signifying that, for each pair of * encountered elements {@code x, y}, {@code !x.equals(y)}. This * applies for example, to a Spliterator based on a {@link Set}. 基于Set的去重DISTINCT public static final int DISTINCT = 0x00000001; * Characteristic value signifying that encounter order follows a defined * sort order. If so, method {@link #getComparator()} returns the associated * Comparator, or {@code null} if all elements are {@link Comparable} and * are sorted by their natural ordering. * * <p>A Spliterator that reports {@code SORTED} must also report * {@code ORDERED}. * 已排序的一定是有序的 * * @apiNote The spliterators for {@code Collection} classes in the JDK that * implement {@link NavigableSet} or {@link SortedSet} report {@code SORTED}. * 如果基于集合的spliterator实现了NavigableSet或SortedSet接口 则为SORTED public static final int SORTED = 0x00000004; * Characteristic value signifying that the value returned from * {@code estimateSize()} prior to traversal or splitting represents a * finite size that, in the absence of structural source modification, * represents an exact count of the number of elements that would be * encountered by a complete traversal. * 源中元素个数有限 源元素结构特性未被修改 estimateSize能在完整遍历过程中 精准计算 public static final int SIZED = 0x00000040; * Characteristic value signifying that the source guarantees that * encountered elements will not be {@code null}. (This applies, * for example, to most concurrent collections, queues, and maps.) * 源中元素都不为null public static final int NONNULL = 0x00000100; * Characteristic value signifying that the element source cannot be * structurally modified; that is, elements cannot be added, replaced, or * removed, so such changes cannot occur during traversal. A Spliterator * that does not report {@code IMMUTABLE} or {@code CONCURRENT} is expected * to have a documented policy (for example throwing * {@link ConcurrentModificationException}) concerning structural * interference detected during traversal. * 源中元素结构不可变 * 源中元素在遍历过程中 不能被 添加 替换(包含修改) 删除 * 如果遍历时 发送元素结构发生改变 则不能表示为IMMUTABLE或CONCURRENT 抛出ConcurrentModificationException public static final int IMMUTABLE = 0x00000400; * Characteristic value signifying that the element source may be safely * concurrently modified (allowing additions, replacements, and/or removals) * by multiple threads without external synchronization. If so, the * Spliterator is expected to have a documented policy concerning the impact * of modifications during traversal. * * <p>A top-level Spliterator should not report both {@code CONCURRENT} and * {@code SIZED}, since the finite size, if known, may change if the source * is concurrently modified during traversal. Such a Spliterator is * inconsistent and no guarantees can be made about any computation using * that Spliterator. Sub-spliterators may report {@code SIZED} if the * sub-split size is known and additions or removals to the source are not * reflected when traversing. * * <p>A top-level Spliterator should not report both {@code CONCURRENT} and * {@code IMMUTABLE}, since they are mutually exclusive. Such a Spliterator * is inconsistent and no guarantees can be made about any computation using * that Spliterator. Sub-spliterators may report {@code IMMUTABLE} if * additions or removals to the source are not reflected when traversing. * * @apiNote Most concurrent collections maintain a consistency policy * guaranteeing accuracy with respect to elements present at the point of * Spliterator construction, but possibly not reflecting subsequent * additions or removals. * 顶层的Spliterator不能同时拥有CONCURRENT和SIZED特性 * 并发时可能存在对源进行添加、替换(修改)、删除 以改变元素个数 * 顶层的Spliterator不能同时拥有CONCURRENT和IMMUTABLE特性 * 这两种特性是互斥的 * 大多数并发集合都保持一致性策略,以确保在拆分器构造点存在的元素的准确性,但可能不反映随后的添加或删除 public static final int CONCURRENT = 0x00001000; * Characteristic value signifying that all Spliterators resulting from * {@code trySplit()} will be both {@link #SIZED} and {@link #SUBSIZED}. * (This means that all child Spliterators, whether direct or indirect, will * be {@code SIZED}.) * * <p>A Spliterator that does not report {@code SIZED} as required by * {@code SUBSIZED} is inconsistent and no guarantees can be made about any * computation using that Spliterator. * * @apiNote Some spliterators, such as the top-level spliterator for an * approximately balanced binary tree, will report {@code SIZED} but not * {@code SUBSIZED}, since it is common to know the size of the entire tree * but not the exact sizes of subtrees. * 顶层二叉树是SIZED 但不是SUBSIZED 因为不知道子树的大小 * 从trySplit返回的子Spliterator都是SIZED 和 SUBSIZED public static final int SUBSIZED = 0x00004000;四、Spliterator方法// 四、Spliterator方法 * If a remaining element exists, performs the given action on it, * returning {@code true}; else returns {@code false}. If this * Spliterator is {@link #ORDERED} the action is performed on the * next element in encounter order. Exceptions thrown by the * action are relayed to the caller. * 尝试遍历: 如果有下一个元素 就对其执行action 如果是有序的 按照元素相遇顺序 对其执行action 如果有异常 将异常信息返回给方法调用者 tryAdvance() 完成了 Iterator的hasNext()与next() boolean tryAdvance(Consumer<? super T> action); * Performs the given action for each remaining element, sequentially in * the current thread, until all elements have been processed or the action * throws an exception. If this Spliterator is {@link #ORDERED}, actions * are performed in encounter order. Exceptions thrown by the action * are relayed to the caller. 按顺序遍历剩余元素 并对每个元素执行action 直到遍历结束 将异常信息返回给方法调用者 default void forEachRemaining(Consumer<? super T> action) { do { } while (tryAdvance(action)); } * If this spliterator can be partitioned, returns a Spliterator * covering elements, that will, upon return from this method, not * be covered by this Spliterator. * * <p>If this Spliterator is {@link #ORDERED}, the returned Spliterator * must cover a strict prefix of the elements. * * <p>Unless this Spliterator covers an infinite number of elements, * repeated calls to {@code trySplit()} must eventually return {@code null}. * Upon non-null return: * <ul> * <li>the value reported for {@code estimateSize()} before splitting, * must, after splitting, be greater than or equal to {@code estimateSize()} * for this and the returned Spliterator; and</li> * <li>if this Spliterator is {@code SUBSIZED}, then {@code estimateSize()} * for this spliterator before splitting must be equal to the sum of * {@code estimateSize()} for this and the returned Spliterator after * splitting.</li> * </ul> * * <p>This method may return {@code null} for any reason, * including emptiness, inability to split after traversal has * commenced, data structure constraints, and efficiency * considerations. * * @apiNote * An ideal {@code trySplit} method efficiently (without * traversal) divides its elements exactly in half, allowing * balanced parallel computation. Many departures from this ideal * remain highly effective; for example, only approximately * splitting an approximately balanced tree, or for a tree in * which leaf nodes may contain either one or two elements, * failing to further split these nodes. However, large * deviations in balance and/or overly inefficient {@code * trySplit} mechanics typically result in poor parallel * performance. 尝试对Spliterator中元素进行trySplit 若能进行拆分,则返回一个新的Spliterator对象 装载已分割的元素 如果分割前有序,分割后也是有序的 分割结果不为null: 进行有限分割后 最终能得到非null元素 分割结果为null: 对有限元素个数的分割:进行无限分割 分割前元素个数为null 遍历开始后无法拆分 数据结构约束 性能考量 Spliterator<T> trySplit(); * Returns an estimate of the number of elements that would be * encountered by a {@link #forEachRemaining} traversal, or returns {@link * Long#MAX_VALUE} if infinite, unknown, or too expensive to compute. * * <p>If this Spliterator is {@link #SIZED} and has not yet been partially * traversed or split, or this Spliterator is {@link #SUBSIZED} and has * not yet been partially traversed, this estimate must be an accurate * count of elements that would be encountered by a complete traversal. * Otherwise, this estimate may be arbitrarily inaccurate, but must decrease * as specified across invocations of {@link #trySplit}. * * @apiNote * Even an inexact estimate is often useful and inexpensive to compute. * For example, a sub-spliterator of an approximately balanced binary tree * may return a value that estimates the number of elements to be half of * that of its parent; if the root Spliterator does not maintain an * accurate count, it could estimate size to be the power of two * corresponding to its maximum depth. 估算元素数量(即将遍历的元素个数) 如果元素数量无限 未知 或计算成本很昂贵 返回Long.Max_Value 如果Spliterator是一个SIZED或SUBSIZED estimate则是完整遍历所需要的值(accurate精确) long estimateSize(); * Convenience method that returns {@link #estimateSize()} if this * Spliterator is {@link #SIZED}, else {@code -1}. characteristic.SIZED –>返回确定的大小 否则为 -1L default long getExactSizeIfKnown() { return (characteristics() & SIZED) == 0 ? -1L : estimateSize(); } * Returns a set of characteristics of this Spliterator and its * elements. The result is represented as ORed values from {@link * #ORDERED}, {@link #DISTINCT}, {@link #SORTED}, {@link #SIZED}, * {@link #NONNULL}, {@link #IMMUTABLE}, {@link #CONCURRENT}, * {@link #SUBSIZED}. Repeated calls to {@code characteristics()} on * a given spliterator, prior to or in-between calls to {@code trySplit}, * should always return the same result. * * <p>If a Spliterator reports an inconsistent set of * characteristics (either those returned from a single invocation * or across multiple invocations), no guarantees can be made * about any computation using this Spliterator. * * @apiNote The characteristics of a given spliterator before splitting * may differ from the characteristics after splitting. For specific * examples see the characteristic values {@link #SIZED}, {@link #SUBSIZED} * and {@link #CONCURRENT}. * * @return a representation of characteristics 返回Spliterator与其元素的一个特性值标识 在分割期间或之前 其元素的特性不变 分割前后若元素的特性发生了变更 对其进行计算行为是不能受到保证的 int characteristics(); * Returns {@code true} if this Spliterator’s {@link * #characteristics} contain all of the given characteristics. 判断是否包含此元素特性 default boolean hasCharacteristics(int characteristics) { return (characteristics() & characteristics) == characteristics; } * If this Spliterator’s source is {@link #SORTED} by a {@link Comparator}, * returns that {@code Comparator}. If the source is {@code SORTED} in * {@linkplain Comparable natural order}, returns {@code null}. Otherwise, * if the source is not {@code SORTED}, throws {@link IllegalStateException}. 如果source是有序的: 如果是按照比较器进行排序 则返回该比较器 如果是Comparable natural order 则返回null 如果source是无序的 抛出IllegalStateException异常 default Comparator<? super T> getComparator() { throw new IllegalStateException(); } * A Spliterator specialized for primitive values. * 针对于原生类型值的特化分割器 * * @param <T> the type of elements returned by this Spliterator. * The type must be a wrapper type for a primitive type, * such as {@code Integer} for the primitive {@code int} type. * @param <T_CONS> the type of primitive consumer. The type must be a * primitive specialization of {@link java.util.function.Consumer} for * {@code T}, such as {@link java.util.function.IntConsumer} for {@code Integer}. * @param <T_SPLITR> the type of primitive Spliterator. The type must be * a primitive specialization of Spliterator for {@code T}, such as * {@link Spliterator.OfInt} for {@code Integer}. * * @see Spliterator.OfInt * @see Spliterator.OfLong * @see Spliterator.OfDouble * @since 1.8 * T Spliterator返回的元素类型:原生包装类型 * T_CONS primitive consumer :java.util.function.IntConsumer对Integer的原生特化 * T_SPLITR primitive Spliterator :Spliterator.OfInt对Integer的原生特化 * public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>> extends Spliterator<T> { @Override T_SPLITR trySplit(); @SuppressWarnings(“overloads”) boolean tryAdvance(T_CONS action); @SuppressWarnings(“overloads”) default void forEachRemaining(T_CONS action) { do { } while (tryAdvance(action)); } } * A Spliterator specialized for {@code int} values. * @since 1.8 public interface OfInt extends OfPrimitive<Integer, IntConsumer, OfInt> { @Override OfInt trySplit(); @Override boolean tryAdvance(IntConsumer action); @Override default void forEachRemaining(IntConsumer action) { do { } while (tryAdvance(action)); }五、Consumer 与 IntConsumer、LongConsumer、DoubleConsumer 五、Consumer 与 IntConsumer、LongConsumer、DoubleConsumer // Consumer 与 IntConsumer 为什么能进行强制类型转换? // Consumer 与 IntConsumer 之间没有继承关系 层次上无关系 // Consumer 与 IntConsumer 当传入的参数是整型int,Integer时 会自动进行装箱拆箱 // ((IntConsumer) action::accept) 是Lambda表达式 // Lambda表达式 是一种匿名函数 没有方法声明 具有上下文自动推测类型功能 * {@inheritDoc} * @implSpec * If the action is an instance of {@code IntConsumer} then it is cast * to {@code IntConsumer} and passed to * {@link #tryAdvance(java.util.function.IntConsumer)}; otherwise * the action is adapted to an instance of {@code IntConsumer}, by * boxing the argument of {@code IntConsumer}, and then passed to * {@link #tryAdvance(java.util.function.IntConsumer)}. @Override default boolean tryAdvance(Consumer<? super Integer> action) { if (action instanceof IntConsumer) { return tryAdvance((IntConsumer) action); } else { if (Tripwire.ENABLED) Tripwire.trip(getClass(), “{0} calling Spliterator.OfInt.tryAdvance((IntConsumer) action::accept)”); return tryAdvance((IntConsumer) action::accept); } } * {@inheritDoc} * @implSpec * If the action is an instance of {@code IntConsumer} then it is cast * to {@code IntConsumer} and passed to * {@link #forEachRemaining(java.util.function.IntConsumer)}; otherwise * the action is adapted to an instance of {@code IntConsumer}, by * boxing the argument of {@code IntConsumer}, and then passed to * {@link #forEachRemaining(java.util.function.IntConsumer)}. @Override default void forEachRemaining(Consumer<? super Integer> action) { if (action instanceof IntConsumer) { forEachRemaining((IntConsumer) action); } else { if (Tripwire.ENABLED) Tripwire.trip(getClass(), “{0} calling Spliterator.OfInt.forEachRemaining((IntConsumer) action::accept)”); forEachRemaining((IntConsumer) action::accept); } } } * A Spliterator specialized for {@code long} values. * @since 1.8 public interface OfLong extends OfPrimitive<Long, LongConsumer, OfLong> { @Override OfLong trySplit(); @Override boolean tryAdvance(LongConsumer action); @Override default void forEachRemaining(LongConsumer action) { do { } while (tryAdvance(action)); } * {@inheritDoc} * @implSpec * If the action is an instance of {@code LongConsumer} then it is cast * to {@code LongConsumer} and passed to * {@link #tryAdvance(java.util.function.LongConsumer)}; otherwise * the action is adapted to an instance of {@code LongConsumer}, by * boxing the argument of {@code LongConsumer}, and then passed to * {@link #tryAdvance(java.util.function.LongConsumer)}. @Override default boolean tryAdvance(Consumer<? super Long> action) { if (action instanceof LongConsumer) { return tryAdvance((LongConsumer) action); } else { if (Tripwire.ENABLED) Tripwire.trip(getClass(), “{0} calling Spliterator.OfLong.tryAdvance((LongConsumer) action::accept)”); return tryAdvance((LongConsumer) action::accept); } } * {@inheritDoc} * @implSpec * If the action is an instance of {@code LongConsumer} then it is cast * to {@code LongConsumer} and passed to * {@link #forEachRemaining(java.util.function.LongConsumer)}; otherwise * the action is adapted to an instance of {@code LongConsumer}, by * boxing the argument of {@code LongConsumer}, and then passed to * {@link #forEachRemaining(java.util.function.LongConsumer)}. @Override default void forEachRemaining(Consumer<? super Long> action) { if (action instanceof LongConsumer) { forEachRemaining((LongConsumer) action); } else { if (Tripwire.ENABLED) Tripwire.trip(getClass(), “{0} calling Spliterator.OfLong.forEachRemaining((LongConsumer) action::accept)”); forEachRemaining((LongConsumer) action::accept); } } } * A Spliterator specialized for {@code double} values. * @since 1.8 public interface OfDouble extends OfPrimitive<Double, DoubleConsumer, OfDouble> { @Override OfDouble trySplit(); @Override boolean tryAdvance(DoubleConsumer action); @Override default void forEachRemaining(DoubleConsumer action) { do { } while (tryAdvance(action)); } * {@inheritDoc} * @implSpec * If the action is an instance of {@code DoubleConsumer} then it is * cast to {@code DoubleConsumer} and passed to * {@link #tryAdvance(java.util.function.DoubleConsumer)}; otherwise * the action is adapted to an instance of {@code DoubleConsumer}, by * boxing the argument of {@code DoubleConsumer}, and then passed to * {@link #tryAdvance(java.util.function.DoubleConsumer)}. @Override default boolean tryAdvance(Consumer<? super Double> action) { if (action instanceof DoubleConsumer) { return tryAdvance((DoubleConsumer) action); } else { if (Tripwire.ENABLED) Tripwire.trip(getClass(), “{0} calling Spliterator.OfDouble.tryAdvance((DoubleConsumer) action::accept)”); return tryAdvance((DoubleConsumer) action::accept); } } * {@inheritDoc} * @implSpec * If the action is an instance of {@code DoubleConsumer} then it is * cast to {@code DoubleConsumer} and passed to * {@link #forEachRemaining(java.util.function.DoubleConsumer)}; * otherwise the action is adapted to an instance of * {@code DoubleConsumer}, by boxing the argument of * {@code DoubleConsumer}, and then passed to * {@link #forEachRemaining(java.util.function.DoubleConsumer)}. @Override default void forEachRemaining(Consumer<? super Double> action) { if (action instanceof DoubleConsumer) { forEachRemaining((DoubleConsumer) action); } else { if (Tripwire.ENABLED) Tripwire.trip(getClass(), “{0} calling Spliterator.OfDouble.forEachRemaining((DoubleConsumer) action::accept)”); forEachRemaining((DoubleConsumer) action::accept); } } }}/六、Consumer 与 IntConsumer 的强制类型转换测试// 六、Consumer 与 IntConsumer 的强制类型转换测试// 传入面向对象 对象this.action(intConsumer);// 传入Lambda表达式 函数式编程this.action(intConsumer::accept);this.action(value -> intConsumer.accept(value));this.action(consumer::accept);this.action(value -> consumer.accept(value));// 面向对象强制类型转换 报错java.lang.ClassCastException// this.action((IntConsumer) consumer);// this.action(((IntConsumer) consumer)::accept);// this.action(t -> ((IntConsumer) consumer).accept(t));// 函数式编程强制类型转换 Lambda表达式没变this.action((IntConsumer) consumer::accept);this.action((IntConsumer) (t -> consumer.accept(t)));this.action((IntConsumer) t -> consumer.accept(t));七、Iterator-based Spliterators 与 StreamSupport底层实现// 七、Iterator-based Spliterators 与 StreamSupport底层实现// Iterator-based Spliterators/ * A Spliterator using a given Iterator for element * operations. The spliterator implements {@code trySplit} to * permit limited parallelism. * spliterator利用trySplit实现有限的并行化操作 * * static class IteratorSpliterator<T> implements Spliterator<T> {} // * Low-level utility methods for creating and manipulating streams. * 用于创建和操作流的底层实用程序方法 * * <p>This class is mostly for library writers presenting stream views * of data structures; most static stream methods intended for end users are in * the various {@code Stream} classes. * StreamSupport提供数据结构的流视图的library 大多数为终端用户使用的静态流方法在Stream类中 * * @since 1.8 * public final class StreamSupport { * Creates a new sequential or parallel {@code Stream} from a * {@code Spliterator}. * * * <p>The spliterator is only traversed, split, or queried for estimated * size after the terminal operation of the stream pipeline commences. * 仅在流管道的终端操作开始后,才遍历、拆分或查询spliterator的估计大小。 * * <p>It is strongly recommended the spliterator report a characteristic of * {@code IMMUTABLE} or {@code CONCURRENT}, or be * <a href="../Spliterator.html#binding">late-binding</a>. Otherwise, * {@link #stream(java.util.function.Supplier, int, boolean)} should be used * to reduce the scope of potential interference with the source. See * <a href=“package-summary.html#NonInterference”>Non-Interference</a> for * more details. * 强烈建议对spliterator设置characteristic(IMMUTABLE CONCURRENT late-binding) * 以减少潜在的干扰源范围 * public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) { Objects.requireNonNull(spliterator); return new ReferencePipeline.Head<>(spliterator, StreamOpFlag.fromCharacteristics(spliterator), parallel); }}/八、流源分析 // 八、流源分析 / 流源的创建 Abstract base class for an intermediate pipeline stage or pipeline source stage implementing whose elements are of type {@code U}. 抽象基类:用于实现其元素类型为{@code U}的中间管道阶段或管道源阶段 ReferencePipeline 操作引用类型 (将源阶段 与 [0,n)个中间操作阶段 看做一个对象) * @param <P_IN> type of elements in the upstream source * @param <P_OUT> type of elements in produced by this stage abstract class ReferencePipeline<P_IN, P_OUT> extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>> implements Stream<P_OUT> { * 源阶段 * Source stage of a ReferencePipeline. * * @param <E_IN> type of elements in the upstream source * @param <E_OUT> type of elements in produced by this stage ReferencePipeline中静态内部类Head static class Head<E_IN, E_OUT> extends ReferencePipeline<E_IN, E_OUT> * * 注意: * 流本身不持有数据 * 数据的持有者:流的数据源(集合、数组等) * 流关注对数据的计算 // 抽象基类: 抽象管道AbstractPipeline 流接口及 其特化的核心实现 管理流管道 创建 与 评估 * Abstract base class for “pipeline” classes, which are the core * implementations of the Stream interface and its primitive specializations. * Manages construction and evaluation of stream pipelines. * * <p>A concrete intermediate stage is generally built from an * {@code AbstractPipeline}, a shape-specific pipeline class which extends it * (e.g., {@code IntPipeline}) which is also abstract, and an operation-specific * concrete class which extends that. {@code AbstractPipeline} contains most of * the mechanics of evaluating the pipeline, and implements methods that will be * used by the operation; the shape-specific classes add helper methods for * dealing with collection of results into the appropriate shape-specific * containers. * * * <p>After chaining a new intermediate operation, or executing a terminal * operation, the stream is considered to be consumed, and no more intermediate * or terminal operations are permitted on this stream instance. * 在链式添加中间操作或一个终止操作后 流视做被消费 * 流只能被消费一次 已消费–>不允许在此流实例中存在更多的中间操作或终止操作 * * @implNote * <p>For sequential streams, and parallel streams without * <a href=“package-summary.html#StreamOps”>stateful intermediate * operations</a>, parallel streams, pipeline evaluation is done in a single * pass that “jams” all the operations together. For parallel streams with * stateful operations, execution is divided into segments, where each * stateful operations marks the end of a segment, and each segment is * evaluated separately and the result used as the input to the next * segment. In all cases, the source data is not consumed until a terminal * operation begins. * 串行流 与 无状态的并行流 * 流的消费 是将中间的操作进行“jams”(打包放一起)对流中每个元素执行action–>single pass * * 有状态的并行流 * 执行分成segments 分别对segment执行有状态操作 并将其结果作为下一个segment输入 * * 在任何情况下,有且只有在一个终止操作被调用时 流真正被消费 abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>> extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> AbstractPipeline的构造方法 AbstractPipeline(Supplier<? extends Spliterator<?>> source, int sourceFlags, boolean parallel) {} AbstractPipeline(Spliterator<?> source, int sourceFlags, boolean parallel) {} 同一时间构造同一个AbstractPipeline 有且只有调用AbstractPipeline构造方法之一 sourceSpliterator与sourceSupplier 同一时间只能存在其一 当流被消费后 若not null 要设置为null 只能被消费一次 private Spliterator<?> sourceSpliterator; private Supplier<? extends Spliterator<?>> sourceSupplier; // 针对于流源的foreach Optimized sequential terminal operations for the head of the pipeline @Override public void forEach(Consumer<? super E_OUT> action) { if (!isParallel()) { sourceStageSpliterator().forEachRemaining(action); } else { super.forEach(action); } } Terminal operations from Stream @Override public void forEach(Consumer<? super P_OUT> action) { evaluate(ForEachOps.makeRef(action, false)); } /九、Array.asList()流源遍历注意事项 // 九、Array.asList()流源遍历注意事项 / 为什么 未调用IteratorSpliterator.forEachRemaining() list.stream().forEach(System.out::println); 执行过程分析 Arrays.asList() private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable{ private final E[] a; ArrayList(E[] array) { a = Objects.requireNonNull(array); } @Override public Spliterator<E> spliterator() { return Spliterators.spliterator(a, Spliterator.ORDERED); } } public static <T> Spliterator<T> spliterator(Object[] array, int additionalCharacteristics) { return new ArraySpliterator<>(Objects.requireNonNull(array), additionalCharacteristics); } @Override public void forEach(Consumer<? super E_OUT> action) { if (!isParallel()) { sourceStageSpliterator().forEachRemaining(action); } else { super.forEach(action); } } @SuppressWarnings(“unchecked”) @Override public void forEachRemaining(Consumer<? super T> action) { Object[] a; int i, hi; // hoist accesses and checks from loop if (action == null) throw new NullPointerException(); if ((a = array).length >= (hi = fence) && (i = index) >= 0 && i < (index = hi)) { do { action.accept((T)a[i]); } while (++i < hi); } }*/ System.out.println(list.getClass()); // Arrays中静态内部类ArrayList (class java.util.Arrays$ArrayList) // @Override public Spliterator<E> spliterator(){} // 调用ArraySpliterator.forEachRemaining()实现 list.stream().forEach(System.out::println); // 普通集合遍历 Iterable 中的 forEach // 效率高 list.forEach(System.out::println); }}十、测试结果 . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / ` | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 18:09:13.662 INFO 2224 — [ main] c.j.d.j.S.S.B.SpliteratorDetail : Starting SpliteratorDetail on DESKTOP-87RMBG4 with PID 2224 (started by 46250 in E:\IdeaProjects\design)2019-02-20 18:09:13.663 INFO 2224 — [ main] c.j.d.j.S.S.B.SpliteratorDetail : No active profile set, falling back to default profiles: default2019-02-20 18:09:14.133 INFO 2224 — [ main] c.j.d.j.S.S.B.SpliteratorDetail : Started SpliteratorDetail in 0.653 seconds (JVM running for 1.335)100100100100100100100100class java.util.Arrays$ArrayListKiritoAsunaSinonYuukiAliceKiritoAsunaSinonYuukiAlice ...

February 20, 2019 · 18 min · jiezi

Stream流与Lambda表达式(三) 静态工厂类Collectors

/** * @author 陈杨 /@SpringBootTest@RunWith(SpringRunner.class)public class CollectorsDetail { private List<String> names; private List<Student> students; private List<List<String>> snames; @Before public void init() { names = Arrays.asList(“Kirito”, “Asuna”, “Sinon”, “Yuuki”, “Alice”); snames = Arrays.asList(Collections.singletonList(“Kirito”), Collections.singletonList(“Asuna”), Collections.singletonList(“Sinon”), Collections.singletonList(“Yuuki”), Collections.singletonList(“Alice”)); students = new Students().init(); } @Test public void testCollectorsDetail() {一、静态工厂类Collectors 实现方式// 一、静态工厂类Collectors 实现方式// Collectors 静态工厂类 最终由CollectorImpl实现// 1、 通过CollectorImpl实现// 2、 通过reducing()实现—> reducing()底层由CollectorImpl实现// Collectors.toList() 是 Collectors.toCollection()的一种具化表现形式// Collectors.joining() 使用StringBuilder.append 拼接字符串二、静态工厂类Collectors 常用收集器// 二、静态工厂类Collectors 常用收集器/ 返回一个(不可修改)unmodifiable List的ArrayList 按照相遇的顺序encounter order@since 10@SuppressWarnings(“unchecked”)public static <T> Collector<T, ?, List<T>> toUnmodifiableList() { return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add, (left, right) -> { left.addAll(right); return left; }, list -> (List<T>)List.of(list.toArray()), CH_NOID);}// 返回一个(不可修改)unmodifiable Set的HashSet 无序@since 10@SuppressWarnings(“unchecked”)public static <T> Collector<T, ?, Set<T>> toUnmodifiableSet() { return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add, (left, right) -> { if (left.size() < right.size()) { right.addAll(left); return right; } else { left.addAll(right); return left; } }, set -> (Set<T>)Set.of(set.toArray()), CH_UNORDERED_NOID);}// 返回一个flatMapping扁平化mapper处理后 由downstream收集的 累加器处理的结果合并 单线程@since 9public static <T, U, A, R> Collector<T, ?, R> flatMapping(Function<? super T, ? extends Stream<? extends U>> mapper, Collector<? super U, A, R> downstream) { BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator(); return new CollectorImpl<>(downstream.supplier(), (r, t) -> { try (Stream<? extends U> result = mapper.apply(t)) { if (result != null) result.sequential().forEach(u -> downstreamAccumulator.accept(r, u)); } }, downstream.combiner(), downstream.finisher(), downstream.characteristics());}// 对符合Predicate预期结果 进行过滤filtering 使用downstream 收集元素@since 9public static <T, A, R> Collector<T, ?, R> filtering(Predicate<? super T> predicate, Collector<? super T, A, R> downstream) { BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator(); return new CollectorImpl<>(downstream.supplier(), (r, t) -> { if (predicate.test(t)) { downstreamAccumulator.accept(r, t); } }, downstream.combiner(), downstream.finisher(), downstream.characteristics());}// 使用Map映射key–>keyMapper–>Key value–>valueMapper–>Value 对映射后的结果进行组装entrySet–>Map<Key,Value> (unmodifiable Map)@since 10@SuppressWarnings({“rawtypes”, “unchecked”})public static <T, K, U> Collector<T, ?, Map<K,U>> toUnmodifiableMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) { Objects.requireNonNull(keyMapper, “keyMapper”); Objects.requireNonNull(valueMapper, “valueMapper”); return collectingAndThen( toMap(keyMapper, valueMapper), map -> (Map<K,U>)Map.ofEntries(map.entrySet().toArray(new Map.Entry[0])));}// 使用Map映射key–>keyMapper–>Key value–>valueMapper–>Value mergeFunction 对相同key 映射后的进行Value 合并操作 对映射后的结果进行组装entrySet–>Map<Key,Value> (unmodifiable Map)@since 10@SuppressWarnings({“rawtypes”, “unchecked”})public static <T, K, U> Collector<T, ?, Map<K,U>> toUnmodifiableMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction) { Objects.requireNonNull(keyMapper, “keyMapper”); Objects.requireNonNull(valueMapper, “valueMapper”); Objects.requireNonNull(mergeFunction, “mergeFunction”); return collectingAndThen( toMap(keyMapper, valueMapper, mergeFunction, HashMap::new), map -> (Map<K,U>)Map.ofEntries(map.entrySet().toArray(new Map.Entry[0])));}/// Collectors 收集器System.out.println("—————————————–\n");// 使用ArrayList集合 按照流中元素排列先后顺序 进行添加操作List<String> unmodifiableList = names.stream().collect(Collectors.toUnmodifiableList());System.out.println(unmodifiableList);System.out.println("—————————————\n");// 使用HashSet集合 对流中元素顺序 进行添加操作Set<String> unmodifiableSet = names.stream().collect(Collectors.toUnmodifiableSet());System.out.println(unmodifiableSet);System.out.println("—————————————\n");// 将集合扁平化展开 对其中的字符串的每个字母 进行大写转换 使用ArrayList对转换后的结果进行收集List<String> strings = snames.stream() .collect(Collectors.flatMapping(list -> list.stream().map(String::toUpperCase), Collectors.toList()));System.out.println(strings);System.out.println("—————————————\n");// 对流中元素进行遍历 对符合预期要求的元素 使用ArrayList集合存放List<String> filteringPredicate = names.stream().collect(Collectors.filtering(“Kirito”::equals, Collectors.toList()));System.out.println(filteringPredicate);System.out.println("—————————————\n");// 对流中元素进行遍历 对key–>keyMapper value–>valueMapper 映射 得到Map集合Map<Integer, List<Student>> listMap = students.stream() .collect(Collectors.toUnmodifiableMap(Student::getId, student -> { List<Student> stus = new ArrayList<>(); stus.add(student); return stus; }));System.out.println(listMap);System.out.println("—————————————\n");// 对流中元素进行遍历 对key–>keyMapper–>Key value–>valueMapper–>Value 映射// 对满足相同Key的元素 Value进行合并Map<Integer, String> lengthName = names.stream() .collect(Collectors.toUnmodifiableMap(String::length, String::toString, (str1, str2) -> str1 + “\t” + str2));System.out.println(lengthName);System.out.println("—————————————\n");三、groupingBy分组// 三、groupingBy分组// 分组 groupingBy// 对T元素按 key进行分组 –> 分组的依据 classifier–> 分组后的集合 value–> 得到 Map<key,value>/Returns a {@code Collector} implementing a “group by” operation oninput elements of type {@code T}, grouping elements according to a classification function, and returning the results in a {@code Map}.<p>The classification function maps elements to some key type {@code K}.The collector produces a {@code Map<K, List<T>>} whose keys are thevalues resulting from applying the classification function to the inputelements, and whose corresponding values are {@code List}s containing the input elements which map to the associated key under the classificationfunction.<p>There are no guarantees on the type, mutability, serializability, orthread-safety of the {@code Map} or {@code List} objects returned.// 按照key对T进行classifier分组 使用List集合收集分组结果 单线程 线程安全public static <T, K> Collector<T, ?, Map<K, List<T>>>groupingBy(Function<? super T, ? extends K> classifier) { return groupingBy(classifier, toList());}// 按照key对T进行classifier分组 使用自定义收集器downstream 收集分组结果 单线程 线程安全public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) { return groupingBy(classifier, HashMap::new, downstream);}// 按照key对T进行classifier分组 使用自定义mapFactory 重置downstream的CollectorImpl实现 使用自定义收集器downstream 收集分组结果 单线程 线程安全public static <T, K, D, A, M extends Map<K, D>>Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream) { Supplier<A> downstreamSupplier = downstream.supplier(); BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator(); BiConsumer<Map<K, A>, T> accumulator = (m, t) -> { K key = Objects.requireNonNull(classifier.apply(t), “element cannot be mapped to a null key”); A container = m.computeIfAbsent(key, k -> downstreamSupplier.get()); downstreamAccumulator.accept(container, t); }; BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner()); @SuppressWarnings(“unchecked”) Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory; // 不进行强制类型转换 重构CollectorImpl实现 if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) { return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID); } else { // 进行强制类型转换 重构CollectorImpl实现 @SuppressWarnings(“unchecked”) Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher(); Function<Map<K, A>, M> finisher = intermediate -> { intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v)); @SuppressWarnings(“unchecked”) M castResult = (M) intermediate; return castResult; }; return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID); }}/// 分组System.out.println("—————————————–\n");// select * from students group by sex ;Map<String, List<Student>> sexStudent = students.stream().collect(Collectors.groupingBy(Student::getSex));System.out.println(sexStudent);System.out.println("—————————————–\n");// select sex, count() from students group by sex ;Map<String, Long> sexCount = students.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.counting()));System.out.println(sexCount);System.out.println("—————————————–\n");// select sex,avg(salary) from students group by sex ;Map<String, Double> avgSexSalary = students.stream().collect (Collectors.groupingBy(Student::getSex, Collectors.averagingDouble(Student::getSalary)));System.out.println(avgSexSalary);System.out.println("—————————————–\n");// 嵌套分组 先根据sex分组 再对结果按照addr进行分组Map<String, Map<String, List<Student>>> NestedGroupBy = students.stream() .collect(Collectors.groupingBy(Student::getSex, Collectors.groupingBy(Student::getAddr)));System.out.println(NestedGroupBy);System.out.println("—————————————–\n");// 使用自定义收集器downstream 按性别分组 使用HashSet进行 结果收集Map<String, HashSet<Student>> sexHashSet = students.stream() .collect(Collectors.groupingBy(Student::getSex, Collectors.toCollection(HashSet::new)));System.out.println(sexHashSet);System.out.println("—————————————–\n");// 使用自定义收集器downstream 按性别分组 使用HashSet进行 结果收集 重置CollectorImpl实现Map<String, HashSet<Student>> sexCustomCollectorImpl = students.stream() .collect(Collectors.groupingBy(Student::getSex, Hashtable::new, Collectors.toCollection(HashSet::new)));System.out.println(sexCustomCollectorImpl);System.out.println("—————————————–\n");四、groupingByConcurrent分组 // 四、groupingByConcurrent分组 // 分组 groupingByConcurrent // 流的适用条件: // 无序 Collector.Characteristics.UNORDERED // 并发 Collector.Characteristics.CONCURRENT // This is a {@link Collector.Characteristics#CONCURRENT concurrent} and // {@link Collector.Characteristics#UNORDERED unordered} Collector. /* 按照key对T进行classifier分组 使用List集合收集分组结果 public static <T, K> Collector<T, ?, ConcurrentMap<K, List<T>>> groupingByConcurrent(Function<? super T, ? extends K> classifier) { return groupingByConcurrent(classifier, ::new, toList()); }// 按照key对T进行classifier分组 使用自定义收集器downstream 收集分组结果public static <T, K, A, D> Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) { return groupingByConcurrent(classifier, ConcurrentHashMap::new, downstream); }/ / 按照key对T进行classifier分组 使用自定义累加器mapFactory 重置downstream的CollectorImpl实现 使用自定义收集器downstream 收集分组结果 public static <T, K, A, D, M extends ConcurrentMap<K, D>> Collector<T, ?, M> groupingByConcurrent(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream) { Supplier<A> downstreamSupplier = downstream.supplier(); BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator(); BinaryOperator<ConcurrentMap<K, A>> merger = Collectors.<K, A, ConcurrentMap<K, A>>mapMerger(downstream.combiner()); @SuppressWarnings(“unchecked”) Supplier<ConcurrentMap<K, A>> mangledFactory = (Supplier<ConcurrentMap<K, A>>) mapFactory; BiConsumer<ConcurrentMap<K, A>, T> accumulator; if (downstream.characteristics().contains(Collector.Characteristics.CONCURRENT)) { accumulator = (m, t) -> { K key = Objects.requireNonNull(classifier.apply(t), “element cannot be mapped to a null key”); A resultContainer = m.computeIfAbsent(key, k -> downstreamSupplier.get()); downstreamAccumulator.accept(resultContainer, t); }; } else { accumulator = (m, t) -> { K key = Objects.requireNonNull(classifier.apply(t), “element cannot be mapped to a null key”); A resultContainer = m.computeIfAbsent(key, k -> downstreamSupplier.get()); // 多个操作–>同时操作同一个结果容器–>同一时间,有且只有一个进行实际操作–>线程同步 synchronized (resultContainer) { downstreamAccumulator.accept(resultContainer, t); } }; } if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) { return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_CONCURRENT_ID); } else { @SuppressWarnings(“unchecked”) Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher(); Function<ConcurrentMap<K, A>, M> finisher = intermediate -> { intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v)); @SuppressWarnings(“unchecked”) M castResult = (M) intermediate; return castResult; }; return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_CONCURRENT_NOID); } }/// 分组 无序 并发System.out.println("—————————————–\n");// 按性别分组 使用ArrayList进行 结果收集ConcurrentMap<String, List<Student>> sexStudentConcurrent = students.stream() .collect(Collectors.groupingByConcurrent(Student::getSex));System.out.println(sexStudentConcurrent);System.out.println("—————————————–\n");// 使用自定义收集器downstream 按性别分组 使用HashSet进行 结果收集ConcurrentMap<String, HashSet<Student>> sexHashSetConcurrent = students.stream() .collect(Collectors.groupingByConcurrent(Student::getSex, Collectors.toCollection(HashSet::new)));System.out.println(sexHashSetConcurrent);System.out.println("—————————————–\n");// 使用自定义收集器downstream 按性别分组 使用HashSet进行 结果收集 重置CollectorImpl实现ConcurrentReferenceHashMap<String, HashSet<Student>> sexCustomCollectorImplConcurrent = students.stream().collect(Collectors.groupingByConcurrent(Student::getSex, ConcurrentReferenceHashMap::new, Collectors.toCollection(HashSet::new)));System.out.println(sexCustomCollectorImplConcurrent);System.out.println("—————————————–\n");五、partitioningBy分区// 五、partitioningBy分区// 分区 partitioningBy/ 对满足预期的条件 进行分区 使用ArrayList 进行结果的收集public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) { return partitioningBy(predicate, toList());}// 对满足预期的条件 进行分区 使用自定义收集器downstream 进行结果的收集public static <T, D, A> Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T, A, D> downstream) { BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator(); BiConsumer<Partition<A>, T> accumulator = (result, t) -> downstreamAccumulator.accept(predicate.test(t) ? result.forTrue : result.forFalse, t); BinaryOperator<A> op = downstream.combiner(); BinaryOperator<Partition<A>> merger = (left, right) -> new Partition<>(op.apply(left.forTrue, right.forTrue), op.apply(left.forFalse, right.forFalse)); Supplier<Partition<A>> supplier = () -> new Partition<>(downstream.supplier().get(), downstream.supplier().get()); if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) { return new CollectorImpl<>(supplier, accumulator, merger, CH_ID); } else { Function<Partition<A>, Map<Boolean, D>> finisher = par -> new Partition<>(downstream.finisher().apply(par.forTrue), downstream.finisher().apply(par.forFalse)); return new CollectorImpl<>(supplier, accumulator, merger, finisher, CH_NOID); }}*/ // 分区 System.out.println("—————————————–\n"); // select * from students partition by (addr == Sword Art Online) ; Map<Boolean, List<Student>> addrPartition = students.stream().collect (Collectors.partitioningBy(student -> student.getAddr().equals(“Sword Art Online”))); System.out.println(addrPartition); System.out.println("—————————————–\n"); // 嵌套分区 先根据sex分区 再对结果按照addr进行分区 Map<Boolean, Map<Boolean, List<Student>>> NestedPartiton = students.stream() .collect(Collectors.partitioningBy(student -> student.getSex().equals(“Male”), Collectors.partitioningBy(student -> student.getAddr().equals(“Sword Art Online”)))); System.out.println(NestedPartiton); System.out.println("—————————————–\n"); // 使用自定义downstream收集器分区 Map<Boolean, HashSet<Student>> addrCustomPartition = students.stream().collect(Collectors.partitioningBy (student -> student.getAddr().equals(“Sword Art Online”), Collectors.toCollection(HashSet::new))); System.out.println(addrCustomPartition); System.out.println("—————————————–\n"); }}六、测试结果 . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / ` | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 16:58:15.870 INFO 13392 — [ main] c.j.d.java8.Stream.CollectorsDetail : Starting CollectorsDetail on DESKTOP-87RMBG4 with PID 13392 (started by 46250 in E:\IdeaProjects\design)2019-02-20 16:58:15.871 INFO 13392 — [ main] c.j.d.java8.Stream.CollectorsDetail : No active profile set, falling back to default profiles: default2019-02-20 16:58:16.383 INFO 13392 — [ main] c.j.d.java8.Stream.CollectorsDetail : Started CollectorsDetail in 0.708 seconds (JVM running for 1.421)—————————————–[Kirito, Asuna, Sinon, Yuuki, Alice]—————————————[Yuuki, Asuna, Kirito, Sinon, Alice]—————————————[KIRITO, ASUNA, SINON, YUUKI, ALICE]—————————————[Kirito]—————————————{5=[Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)], 4=[Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8)], 3=[Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8)], 2=[Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)], 1=[Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)]}—————————————{6=Kirito, 5=Asuna Sinon Yuuki Alice}——————————————————————————–{Female=[Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8), Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)], Male=[Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)]}—————————————–{Female=4, Male=1}—————————————–{Female=9.99999999E8, Male=9.99999999E8}—————————————–{Female={Alicization=[Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)], Sword Art Online=[Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)], Gun Gale Online=[Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8)], Alfheim Online=[Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8)]}, Male={Sword Art Online=[Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)]}}—————————————–{Female=[Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8), Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)], Male=[Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)]}—————————————–{Female=[Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8), Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)], Male=[Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)]}———————————————————————————-{Male=[Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)], Female=[Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8), Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)]}—————————————–{Male=[Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)], Female=[Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8), Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)]}—————————————–{Female=[Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8), Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)], Male=[Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)]}———————————————————————————-{false=[Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)], true=[Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8), Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)]}—————————————–{false={false=[Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)], true=[Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)]}, true={false=[], true=[Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)]}}—————————————–{false=[Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)], true=[Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8), Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)]}—————————————– ...

February 20, 2019 · 9 min · jiezi

Stream流与Lambda表达式(四) 自定义收集器

一、自定义SetCustomCollector收集器package com.java.design.Stream.CustomCollector;import java.util.*;import java.util.function.BiConsumer;import java.util.function.BinaryOperator;import java.util.function.Function;import java.util.function.Supplier;import java.util.stream.Collector;/** * @author 陈杨 /// 将List集合转换为Set集合 存放相同元素public class SetCustomCollector<T> implements Collector<T, Set<T>, Set<T>> { @Override public Supplier<Set<T>> supplier() { System.out.println(“supplier invoked!”); // return TreeSet::new; return HashSet::new; } @Override public BiConsumer<Set<T>, T> accumulator() { System.out.println(“accumulator invoked!”); return Set<T>::add; } @Override public BinaryOperator<Set<T>> combiner() { System.out.println(“combiner invoked!”); return (first, last) -> { first.addAll(last); return first; }; } @Override public Function<Set<T>, Set<T>> finisher() { System.out.println(“finisher invoked!”); return Function.identity(); } @Override public Set<Characteristics> characteristics() { System.out.println(“characteristics invoked!”); return Collections.unmodifiableSet(EnumSet.of (Characteristics.IDENTITY_FINISH, Characteristics.UNORDERED)); // return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED)); }}二、自定义StudentCustomCollector收集器package com.java.design.Stream.CustomCollector;import com.java.design.java8.entity.Student;import java.util.;import java.util.function.BiConsumer;import java.util.function.BinaryOperator;import java.util.function.Function;import java.util.function.Supplier;import java.util.stream.Collector;/** * @author 陈杨 /// 将学生对象 按照HashMap<Integer,Student> 存放 sid studentpublic class StudentCustomCollector implements Collector<Student, List<Student>, Map<Integer, Student>> { @Override public Supplier<List<Student>> supplier() { System.out.println(“supplier invoked!”); return ArrayList::new; } @Override public BiConsumer<List<Student>, Student> accumulator() { System.out.println(“accumulator invoked!”); return (list, student) -> { System.out.println(“accumulator:” + Thread.currentThread().getName()); list.add(student); }; } @Override public BinaryOperator<List<Student>> combiner() { System.out.println(“combiner invoked!”); return (first, last) -> { first.addAll(last); return first; }; } @Override public Function<List<Student>, Map<Integer, Student>> finisher() { System.out.println(“finisher invoked!”); return list -> { Map<Integer, Student> map = new HashMap<>(); list.forEach(student -> map.put(student.getId(), student)); return map; }; } @Override public Set<Characteristics> characteristics() { System.out.println(“Characteristics invoked!”); return Collections.unmodifiableSet(EnumSet.of(Characteristics.CONCURRENT)); } // Characteristics.IDENTITY_FINISH 从中间容器数据类型 转换为 结果类型 数据类型一致 // 若不一致 抛出类型转换异常 finisher对中间容器数据–>结果类型 进行强制类型转换 // Characteristics.CONCURRENT 多个线程同时操作同一个容器 –> 并行 // Indicates that this collector is <em>concurrent</em>, meaning that // the result container can support the accumulator function being // called concurrently with the same result container from multiple threads. // parallelStream (多线程)并行流 操作 多个结果容器 –> 执行combiner // Characteristics.CONCURRENT + parallelStream 结果容器只有1个 —> 不执行 combiner // ConcurrentModificationException 并发修改异常 // 注意:并行情况下 累加器对结果容器执行单一操作 // 不要在累加器返回的函数式接口实例中做额外的操作 // 不能打印集合类容 同时向集合里添加新元素 // This exception may be thrown by methods that have detected concurrent // modification of an object when such modification is not permissible}三、SetCustomCollectorTest测试package com.java.design.java8.Stream.CustomCollector;import com.java.design.Stream.CustomCollector.SetCustomCollector;import com.java.design.java8.entity.Student;import com.java.design.java8.entity.Students;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;import java.util.Set;/* * @author 陈杨 /@SpringBootTest@RunWith(SpringRunner.class)public class SetCustomCollectorTest { private List<Student> students; @Before public void init() { students = new Students().init(); } @Test public void testSetCustomCollector() { Set<Student> set = students.stream().collect(new SetCustomCollector<>()); System.out.println(set); } /public static <T, I> TerminalOp<T, I> makeRef(Collector<? super T, I, ?> collector) { Supplier<I> supplier = Objects.requireNonNull(collector).supplier(); BiConsumer<I, ? super T> accumulator = collector.accumulator(); BinaryOperator<I> combiner = collector.combiner(); class ReducingSink extends Box<I> implements AccumulatingSink<T, I, ReducingSink> { @Override public void begin(long size) { state = supplier.get(); } @Override public void accept(T t) { accumulator.accept(state, t); } @Override public void combine(ReducingSink other) { state = combiner.apply(state, other.state); } } return new ReduceOp<T, I, ReducingSink>(StreamShape.REFERENCE) { @Override public ReducingSink makeSink() { return new ReducingSink(); } @Override public int getOpFlags() { return collector.characteristics().contains(Collector.Characteristics.UNORDERED) ? StreamOpFlag.NOT_ORDERED : 0; } }; }/ /public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) { A container; if (isParallel() && (collector.characteristics().contains(Collector.Characteristics.CONCURRENT)) && (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) { container = collector.supplier().get(); BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator(); forEach(u -> accumulator.accept(container, u)); } else { container = evaluate(ReduceOps.makeRef(collector)); } return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH) ? (R) container : collector.finisher().apply(container); }/ // 执行流程 方法调用顺序 // container = evaluate(ReduceOps.makeRef(collector)); // Supplier<I> supplier = Objects.requireNonNull(collector).supplier(); // BiConsumer<I, ? super T> accumulator = collector.accumulator(); // BinaryOperator<I> combiner = collector.combiner(); // return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)是否有序 // return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)是否包含IDENTITY_FINISH // ? (R) container 注意强制类型转换 (中间类型 与 返回结果类型) // 注意强制类型转换 /CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner, Set<Characteristics> characteristics) { this(supplier, accumulator, combiner, castingIdentity(), characteristics); } @SuppressWarnings(“unchecked”) private static <I, R> Function<I, R> castingIdentity() { return i -> (R) i; }/ // EnumSet.of(Characteristics.IDENTITY_FINISH, Characteristics.UNORDERED) // 包含 IDENTITY_FINISH 打印结果 // supplier invoked! // accumulator invoked! // combiner invoked! // characteristics invoked! // characteristics invoked! // Set<Student>集合对象 // EnumSet.of(Characteristics.UNORDERED) // 不包含 IDENTITY_FINISH 打印结果 // supplier invoked! // accumulator invoked! // combiner invoked! // characteristics invoked! // characteristics invoked! // finisher invoked! // Set<Student>集合对象}四、StudentCustomCollectorTest测试package com.java.design.java8.Stream.CustomCollector;import com.java.design.Stream.CustomCollector.StudentCustomCollector;import com.java.design.java8.entity.Student;import com.java.design.java8.entity.Students;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;import java.util.Map;/* * @author 陈杨 */@SpringBootTest@RunWith(SpringRunner.class)public class StudentCustomCollectorTest { private List<Student> students; @Before public void init() { students = new Students().init(); } @Test public void testStudentCustomCollectorTest() { System.out.println(“单线程”); Map<Integer, Student> sequentialMap = students.stream().collect(new StudentCustomCollector()); System.out.println(“串行流执行效果:\n—————————————\n”+sequentialMap); System.out.println("—————————————\n"); System.out.println(“多线程”); Map<Integer, Student> parallelMap = students.parallelStream().collect(new StudentCustomCollector()); System.out.println(“并行流执行效果:\n—————————————\n”+parallelMap); System.out.println("—————————————\n"); }}五、测试结果 SetCustomCollectorTest测试结果 . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 17:14:45.547 INFO 3260 --- [ main] c.j.d.j.S.C.SetCustomCollectorTest : Starting SetCustomCollectorTest on DESKTOP-87RMBG4 with PID 3260 (started by 46250 in E:\IdeaProjects\design)2019-02-20 17:14:45.548 INFO 3260 --- [ main] c.j.d.j.S.C.SetCustomCollectorTest : No active profile set, falling back to default profiles: default2019-02-20 17:14:46.055 INFO 3260 --- [ main] c.j.d.j.S.C.SetCustomCollectorTest : Started SetCustomCollectorTest in 0.686 seconds (JVM running for 1.43)supplier invoked!accumulator invoked!combiner invoked!characteristics invoked!characteristics invoked![Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8), Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8), Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)] StudentCustomCollectorTest测试 . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _ | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 17:15:52.817 INFO 3292 — [ main] c.j.d.j.S.C.StudentCustomCollectorTest : Starting StudentCustomCollectorTest on DESKTOP-87RMBG4 with PID 3292 (started by 46250 in E:\IdeaProjects\design)2019-02-20 17:15:52.818 INFO 3292 — [ main] c.j.d.j.S.C.StudentCustomCollectorTest : No active profile set, falling back to default profiles: default2019-02-20 17:15:53.354 INFO 3292 — [ main] c.j.d.j.S.C.StudentCustomCollectorTest : Started StudentCustomCollectorTest in 0.745 seconds (JVM running for 1.439)单线程supplier invoked!accumulator invoked!combiner invoked!Characteristics invoked!accumulator:mainaccumulator:mainaccumulator:mainaccumulator:mainaccumulator:mainCharacteristics invoked!finisher invoked!串行流执行效果:—————————————{1=Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8), 2=Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8), 3=Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), 4=Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), 5=Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)}—————————————多线程Characteristics invoked!Characteristics invoked!supplier invoked!accumulator invoked!combiner invoked!Characteristics invoked!accumulator:mainaccumulator:ForkJoinPool.commonPool-worker-5accumulator:ForkJoinPool.commonPool-worker-5accumulator:ForkJoinPool.commonPool-worker-3accumulator:mainCharacteristics invoked!finisher invoked!并行流执行效果:—————————————{1=Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8), 2=Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8), 3=Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8), 4=Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8), 5=Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)}————————————— ...

February 20, 2019 · 5 min · jiezi

Stream流与Lambda表达式(一) 杂谈

一、流 转换为数组、集合package com.java.design.java8.Stream;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.ArrayList;import java.util.List;import java.util.stream.Stream;/** * @author 陈杨 /@SpringBootTest@RunWith(SpringRunner.class)public class ListChange { private Stream<String> stream = Stream.of(“Kirito”, “Asuna”, “Illyasviel”, “Sakura”); @Test public void testListChange() { // 将流转换为数组 // System.out.println("————-将流转换为数组—————"); // String[] array = stream.toArray(len -> new String[len]); // String[] array = stream.toArray(String[]::new); // Arrays.asList(array).stream().forEach(System.out::println); // 将流转换为集合 // System.out.println("————-将流转换为集合—————"); // System.out.println("——-Collectors.toList()解析———–"); / public static <T> * Collector<T, ?, List<T>> toList() { * return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add, * (left, right) -> { left.addAll(right); return left; }, * CH_ID); }/ // List<String> list = stream.collect(Collectors.toList()); // List<String> linkedList = stream.collect(LinkedList::new,LinkedList::add,LinkedList::addAll); List<String> list = stream.collect(ArrayList::new, ArrayList::add, ArrayList::addAll); list.forEach(System.out::println); System.out.println(list.getClass()); // System.out.println("——-Collectors.toCollection()解析—–"); / public static <T, C extends Collection<T>> * Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) { * return new CollectorImpl<>(collectionFactory, Collection<T>::add, * (r1, r2) -> { r1.addAll(r2); return r1; }, * CH_ID); }*/ // List<String> list =stream.collect(Collectors.toCollection(ArrayList::new)); // List<String> linkedList =stream.collect(Collectors.toCollection(ArrayList::new)); // Set<String> treeSet =stream.collect(Collectors.toCollection(TreeSet::new)); // Set<String> hashSet =stream.collect(Collectors.toCollection(HashSet::new)); }} . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 15:47:22.310 INFO 1348 --- [ main] com.java.design.java8.Stream.ListChange : Starting ListChange on DESKTOP-87RMBG4 with PID 1348 (started by 46250 in E:\IdeaProjects\design)2019-02-20 15:47:22.311 INFO 1348 --- [ main] com.java.design.java8.Stream.ListChange : No active profile set, falling back to default profiles: default2019-02-20 15:47:22.947 INFO 1348 --- [ main] com.java.design.java8.Stream.ListChange : Started ListChange in 0.914 seconds (JVM running for 1.774)KiritoAsunaIllyasvielSakuraclass java.util.ArrayList二、集合排序package com.java.design.java8.Stream;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.*;/** * @author 陈杨 */@SpringBootTest@RunWith(SpringRunner.class)public class ComparatorDetail { private List&lt;String&gt; names; @Before public void init() { names = Arrays.asList("Kirito", "Asuna", "Sinon", "Yuuki", "Alice"); } public void println() { System.out.println(names); System.out.println("-----------------------------------------\n"); } @Test public void testComparatorDetail() { // 对名字进行升序排序 Collections.sort(names); this.println(); // 对名字进行降序排序 names.sort(Collections.reverseOrder()); this.println(); // 按照姓名的字符串长度升序排序 相同长度--&gt;比较前两个字符--&gt;按照字符的ASCII码大小升序排序 names.sort(Comparator.comparingInt(String::length) .thenComparing(str -&gt; str.charAt(0)) .thenComparing(str -&gt; str.charAt(1)) ); this.println(); // 按照姓名的字符串长度降序排序 相同长度--&gt;比较前两个字符--&gt;按照字符的ASCII码大小降序排序 names.sort(Comparator.comparingInt(String::length) .thenComparing(str -&gt; str.charAt(0)) .thenComparing(str -&gt; str.charAt(1)) .reversed()); this.println(); // 按照姓名的字符串长度降序排序 相同长度--&gt;按照字符的ASCII码大小排序(不区分大小写) names.sort(Comparator.comparingInt(String::length) .thenComparing(String.CASE_INSENSITIVE_ORDER)); this.println(); }} . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _ | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 15:58:39.959 INFO 4588 — [ main] c.j.d.java8.Stream.ComparatorDetail : Starting ComparatorDetail on DESKTOP-87RMBG4 with PID 4588 (started by 46250 in E:\IdeaProjects\design)2019-02-20 15:58:39.962 INFO 4588 — [ main] c.j.d.java8.Stream.ComparatorDetail : No active profile set, falling back to default profiles: default2019-02-20 15:58:40.459 INFO 4588 — [ main] c.j.d.java8.Stream.ComparatorDetail : Started ComparatorDetail in 0.729 seconds (JVM running for 1.462)[Alice, Asuna, Kirito, Sinon, Yuuki]—————————————–[Yuuki, Sinon, Kirito, Asuna, Alice]—————————————–[Alice, Asuna, Sinon, Yuuki, Kirito]—————————————–[Kirito, Yuuki, Sinon, Asuna, Alice]—————————————–[Alice, Asuna, Sinon, Yuuki, Kirito]—————————————–三、Stream之map(Lambda)package com.java.design.java8.Stream;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.Arrays;import java.util.Collections;import java.util.List;import java.util.stream.Collectors;/** * @author 陈杨 /@SpringBootTest@RunWith(SpringRunner.class)public class StringOperation { private List<String> list = Arrays.asList(“Kirito”, “Asuna”, “Illyasviel”, “Sakura”); private List<List<String>> listMap = Arrays.asList(Collections.singletonList(“Kirito”), Collections.singletonList(“Asuna”), Collections.singletonList(“Illyasviel”), Collections.singletonList(“Sakura”)); private List<List<String>> listFlatMap = Arrays.asList(Collections.singletonList(“Kirito”), Collections.singletonList(“Asuna”), Collections.singletonList(“Illyasviel”), Collections.singletonList(“Sakura”)); @Test public void testStringOperation() { // 集合中 每个字符串 按照排列先后顺序拼接 形成一个长字符串 // String concat = // list.stream().collect(StringBuilder::new, StringBuilder::append, StringBuilder::append).toString(); // String concat = list.stream().collect(Collectors.joining()); // System.out.println(concat); // 集合中 对每个字符串元素 将所有字母变成大写字母 System.out.println("—————————————–\n"); List<String> upperCase = list.stream().map(String::toUpperCase).collect(Collectors.toList()); upperCase.forEach(System.out::println); // 集合中 对每个字符串元素 将所有字母变成小写字母 System.out.println("—————————————–\n"); List<String> lowerCase = list.stream().map(String::toLowerCase).collect(Collectors.toList()); lowerCase.forEach(System.out::println); System.out.println("—————————————–\n"); System.out.println(“FlatMap与Map的区别:\n”); // map: 对多个list 分别map Fuction 映射 形成多个list System.out.println("—————————————–\n"); System.out.println(“进行map映射:”); List<List<String>> upperMap = listMap.stream() .map(list -> list.stream().map(String::toUpperCase) .collect(Collectors.toList())).collect(Collectors.toList()); upperMap.forEach(System.out::println); // FlatMap: 对多个list 进行flat扁平化 后再进行map Fuction 映射 形成一个list System.out.println("—————————————–\n"); System.out.println(“FlatMap扁平化进行map映射:”); List<String> upperFlatMap = listFlatMap.stream() .flatMap(list -> list.stream().map(String::toUpperCase)).collect(Collectors.toList()); upperFlatMap.forEach(System.out::println); }} . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 15:50:07.423 INFO 8208 --- [ main] c.j.design.java8.Stream.StringOperation : Starting StringOperation on DESKTOP-87RMBG4 with PID 8208 (started by 46250 in E:\IdeaProjects\design)2019-02-20 15:50:07.424 INFO 8208 --- [ main] c.j.design.java8.Stream.StringOperation : No active profile set, falling back to default profiles: default2019-02-20 15:50:07.917 INFO 8208 --- [ main] c.j.design.java8.Stream.StringOperation : Started StringOperation in 0.717 seconds (JVM running for 1.5)-----------------------------------------KIRITOASUNAILLYASVIELSAKURA-----------------------------------------kiritoasunaillyasvielsakura-----------------------------------------FlatMap与Map的区别:-----------------------------------------进行map映射:[KIRITO][ASUNA][ILLYASVIEL][SAKURA]-----------------------------------------FlatMap扁平化进行map映射:KIRITOASUNAILLYASVIELSAKURA四、内部迭代与外部迭代package com.java.design.java8.Stream;import com.java.design.java8.entity.Student;import com.java.design.java8.entity.Students;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.*;/** * @author 陈杨 */@SpringBootTest@RunWith(SpringRunner.class)//迭代本质public class IterativeEssence { private List&lt;Student&gt; students; @Before public void init() { students = new Students().init(); } @Test public void testIterativeEssence() { // 需求: select name from students where age &gt; 14 and addr ="Sword Art Online" order by id desc ; // 外部迭代 System.out.println("-----------------------------------------\n"); System.out.println("外部迭代"); List&lt;Student&gt; list = new ArrayList&lt;&gt;(); for (Student student : students) { if (student.getAge() &gt; 14 &amp;&amp; student.getAddr().equals("Sword Art Online")) { list.add(student); } } list.sort(Comparator.comparingInt(Student::getId)); for (Student student : list) { System.out.println(student.getName()); } // 内部迭代 System.out.println("-----------------------------------------\n"); System.out.println("内部迭代"); students.stream() .filter(student -&gt; student.getAge() &gt; 14) .filter(student -&gt; student.getAddr().equals("Sword Art Online")) .sorted(Comparator.comparingInt(Student::getId)). forEach(student -&gt; System.out.println(student.getName())); // 备注: // 内部迭代与SQL语句属于描述性语言 // 集合关注的是数据与数据存储 // 流关注的是数据的计算 // 流中间操作返回的都是Stream对象 泛型取决于中间操作的类型 // 流终止操作: 无返回值(forEach) 返回值是其他类型 }} . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _ | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 15:53:03.633 INFO 8864 — [ main] c.j.d.java8.Stream.IterativeEssence : Starting IterativeEssence on DESKTOP-87RMBG4 with PID 8864 (started by 46250 in E:\IdeaProjects\design)2019-02-20 15:53:03.640 INFO 8864 — [ main] c.j.d.java8.Stream.IterativeEssence : No active profile set, falling back to default profiles: default2019-02-20 15:53:04.167 INFO 8864 — [ main] c.j.d.java8.Stream.IterativeEssence : Started IterativeEssence in 0.746 seconds (JVM running for 1.455)—————————————–外部迭代KiritoAsuna—————————————–内部迭代KiritoAsuna五、串行流与并行流 简单性能测试package com.java.design.java8.Stream;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.UUID;import java.util.concurrent.TimeUnit;import java.util.function.Function;import java.util.stream.Collectors;import java.util.stream.IntStream;/* * @author 陈杨 /@RunWith(SpringRunner.class)@SpringBootTestpublic class ErgodicString { private List<String> uuid; private long startTime; private long endTime; private long parallelEndTime; @Before public void init() { uuid = new ArrayList<>(10000000); IntStream.range(0, 10000000).forEach(i -> uuid.add(UUID.randomUUID().toString())); } public void testNormal() { startTime = System.nanoTime(); uuid.stream().sorted().collect(Collectors.toList()); endTime = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime); System.out.println(“单线程” + millis); } public void testParallel() { startTime = System.nanoTime(); uuid.parallelStream().sorted().collect(Collectors.toList()); parallelEndTime = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(parallelEndTime - startTime); System.out.println(“多线程” + millis); } @Test public void testErgodicString() { List<String> list = Arrays.asList(“Kirito”, “Asuna”, “Illyasviel”, “Sakura”); // 需求: 将数组中每个元素各个字母大写 并放入集合 System.out.println("——————–串行流stream———————"); // spliterator 分割迭代器 // 串行流stream() 单线程处理 / * default Stream<E> stream() { * return StreamSupport.stream(spliterator(), false);} / // List<String> collect = list.stream().map(str -> str.toUpperCase()).collect(Collectors.toList()); // R apply(T t); toUpperCase() 没有参数作为输入 // 但把调用toUpperCase的对象作为T作为输入 返回的R是return的对象结果 // List<String> collect = list.stream().map(String::toUpperCase).collect(Collectors.toList()); Function<String, String> function = String::toUpperCase; //等价于 (String str) -> str.toUpperCase() //方法引用 类的类型::实例方法 对应的lambda表达式 第一个输入参数 是调用此方法的对象 List<String> collect = list.stream().map(function).collect(Collectors.toList()); collect.forEach(System.out::println); this.testNormal(); System.out.println("—————–并行流parallelStream——————"); // 并行流parallelStream() 多线程处理 / * default Stream<E> parallelStream() { * return StreamSupport.stream(spliterator(), true);} */ List<String> parallelCollect = list.parallelStream().map(str -> str.toUpperCase()).collect(Collectors.toList()); parallelCollect.forEach(System.out::println); this.testParallel(); }} . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / ` | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 15:54:54.321 INFO 7356 — [ main] c.j.design.java8.Stream.ErgodicString : Starting ErgodicString on DESKTOP-87RMBG4 with PID 7356 (started by 46250 in E:\IdeaProjects\design)2019-02-20 15:54:54.323 INFO 7356 — [ main] c.j.design.java8.Stream.ErgodicString : No active profile set, falling back to default profiles: default2019-02-20 15:54:54.817 INFO 7356 — [ main] c.j.design.java8.Stream.ErgodicString : Started ErgodicString in 0.705 seconds (JVM running for 1.528)——————–串行流stream———————KIRITOASUNAILLYASVIELSAKURA单线程10590—————–并行流parallelStream——————KIRITOASUNAILLYASVIELSAKURA多线程3313 ...

February 20, 2019 · 7 min · jiezi

Stream流与Lambda表达式(二) Stream收集器 Collector接口

一、Stream收集器 Collector接口package com.java.design.java8.Stream;import com.java.design.java8.entity.Student;import com.java.design.java8.entity.Students;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.;import java.util.stream.Collectors;/** * @author 陈杨 /@SpringBootTest@RunWith(SpringRunner.class)public class CollectorDetail { private List<Student> students; @Before public void init() { students=new Students().init(); } @Test public void testCollectorDetail() { // Collect 收集器 —- Collector接口 // T–>汇聚操作的元素类型 即流中元素类型 // A–>汇聚操作的可变累积类型 // R–>汇聚操作的结果类型 // public interface Collector<T, A, R> // Collector接口 一种可变汇聚操作 // 将输入元素累积到可变结果容器中 // 在处理完所有输入元素后 可以选择将累积的结果转换为最终表示(可选操作) // 归约操作支持串行与并行 // A mutable reduction operation that accumulates input elements into a mutable result container, // optionally transforming the accumulated result into a final representation after all input elements // have been processed. Reduction operations can be performed either sequentially or in parallel. // Collectors 提供 Collector 汇聚实现 实际上是一个Collector工厂 // The class {@link Collectors} provides implementations of many common mutable reductions.二、Collector 接口组成 // Collector 由以下4个函数协同累积到容器 可选的执行最终转换 // supplier 创建一个新的结果容器 // accumulator累加器 将新数据元素合并到结果容器中 // combiner 合并结果容器 处理线程并发 // finisher 对容器执行可选的最终转换 // // A {@code Collector} is specified by four functions that work together to // accumulate entries into a mutable result container, and optionally perform // a final transform on the result. They are: // creation of a new result container ({@link #supplier()}) // incorporating a new data element into a result container ({@link #accumulator()}) // combining two result containers into one ({@link #combiner()}) // performing an optional final transform on the container ({@link #finisher()})三、combiner / * A function that accepts two partial results and merges them. The * combiner function may fold state from one argument into the other and * return that, or may return a new result container. * * * BinaryOperator<A> combiner(); / / supplier创建单个结果容器–>accumulator调用累积功能–>partition结果–分区容器–>combiner合并分区容器 A sequential implementation of a reduction using a collector would create a single result container using the supplier function, and invoke the accumulator function once for each input element. A parallel implementation would partition the input, create a result container for each partition, accumulate the contents of each partition into a subresult for that partition, and then use the combiner function to merge the subresults into a combined result. /四、identity associativity 约束/ 确保串行与并行结果的一致性,满足约束: identity associativity To ensure that sequential and parallel executions produce equivalent results, the collector functions must satisfy an identity and an associativity constraints. // identity 约束: 对于任何部分累积的结果, 将其与空结果容器组合必须生成等效的结果 a == combiner.apply(a, supplier.get()) The identity constraint says that for any partially accumulated result, combining it with an empty result container must produce an equivalent result. That is, for a partially accumulated result {@code a} that is the result of any series of accumulator and combiner invocations, {@code a} must be equivalent to {@code combiner.apply(a, supplier.get())}. // associativity 约束: 串行计算与并行拆分计算必须产生同等的结果 The associativity constraint says that splitting the computation must produce an equivalent result. That is, for any input elements {@code t1} and {@code t2}, the results {@code r1} and {@code r2} in the computation below must be equivalent: A a1 = supplier.get(); accumulator.accept(a1, t1); accumulator.accept(a1, t2); R r1 = finisher.apply(a1); // result without splitting A a2 = supplier.get(); accumulator.accept(a2, t1); A a3 = supplier.get(); accumulator.accept(a3, t2); R r2 = finisher.apply(combiner.apply(a2, a3)); // result with splitting /五、reduction 汇聚 的实现方式 // reduction 汇聚 的实现方式 // list.stream().reduce() 对象不可变 // list.stream().collect(Collectors.reducing()) 对象可变 // 单线程可以实现结果一致 但在多线程中就会出现错误 / Libraries that implement reduction based on {@code Collector}, such as {@link Stream#collect(Collector)}, must adhere to the following constraints: 传递给accumulator的第一个参数,传递给combiner的二个参数,传递给finisher的参数 必须是函数(supplier accumulator combiner)上一次调用结果 理解: 参数类型A Supplier<A> supplier(); BiConsumer<A, T> accumulator(); BinaryOperator<A> combiner(); Function<A, R> finisher(); The first argument passed to the accumulator function, both arguments passed to the combiner function, and the argument passed to the finisher function must be the result of a previous invocation of the result supplier, accumulator, or combiner functions supplier accumulator combiner的实现结果–> 传递给下一次supplier accumulator combiner操作 或返还给汇聚操作的调用方 而不进行其他操作 The implementation should not do anything with the result of any of the result supplier, accumulator, or combiner functions other than to pass them again to the accumulator, combiner, or finisher functions, or return them to the caller of the reduction operation 一个结果传递给combiner finisher而相同的对象没有从此函数中返回 这个结果不会再被使用 这个传入结果是被消费了 生成了新的对象 If a result is passed to the combiner or finisher function, and the same object is not returned from that function, it is never used again 一旦结果传递给combiner finisher 则不再会传递给accumulator 说明流中元素已经传递完全 accumulator任务已执行完毕 Once a result is passed to the combiner or finisher function, it is never passed to the accumulator function again 非并发单线程 For non-concurrent collectors, any result returned from the result supplier, accumulator, or combiner functions must be serially thread-confined. This enables collection to occur in parallel without the {@code Collector} needing to implement any additional synchronization. The reduction implementation must manage that the input is properly partitioned, that partitions are processed in isolation, and combining happens only after accumulation is complete 并发多线程 For concurrent collectors, an implementation is free to (but not required to) implement reduction concurrently. A concurrent reduction is one where the accumulator function is called concurrently from multiple threads, using the same concurrently-modifiable result container, rather than keeping the result isolated during accumulation. A concurrent reduction should only be applied if the collector has the {@link Characteristics#UNORDERED} characteristics or if the originating data is unordered /六、Characteristics对Collectors的性能优化 / Characteristics对Collectors的性能优化 * * Collectors also have a set of characteristics, that provide hints that can be used by a * reduction implementation to provide better performance. * * * Characteristics indicating properties of a {@code Collector}, which can * be used to optimize reduction implementations. * * enum Characteristics { * * Indicates that this collector is <em>concurrent</em>, meaning that * the result container can support the accumulator function being * called concurrently with the same result container from multiple * threads. * * If a {@code CONCURRENT} collector is not also {@code UNORDERED}, * then it should only be evaluated concurrently if applied to an * unordered data source. CONCURRENT, 多线程处理并发 一定要保证线程安全 使用无序数据源 与UNORDERED联合使用 * Indicates that the collection operation does not commit to preserving * the encounter order of input elements. (This might be true if the * result container has no intrinsic order, such as a {@link Set}.) UNORDERED, 无序集合 * Indicates that the finisher function is the identity function and * can be elided. If set, it must be the case that an unchecked cast * from A to R will succeed. IDENTITY_FINISH 强制类型转换 }/七、Collector接口与 Collectors // Collectors—> Collector接口简单实现 静态内部类CollectorImpl // 为什么要在Collectors类内部定义一个静态内部类CollectorImpl: // Collectors是一个工厂、辅助类 方法的定义是静态的 // 以类名直接调用方法的方式向developer提供最常见的Collector实现 其实现方式是CollectorImpl // CollectorImpl类 有且仅有在 Collectors类 中使用 所以放在一起八、测试方法: // Accumulate names into a List 将学生姓名累积成ArrayList集合 List<String> snameList = students.stream() .map(Student::getName).collect(Collectors.toList()); System.out.println(“将学生姓名累积成ArrayList集合:” + snameList.getClass()); System.out.println(snameList); System.out.println("—————————————–\n"); // Accumulate names into a TreeSet 将学生姓名累积成TreeSet集合 Set<String> snameTree = students.stream() .map(Student::getName).collect(Collectors.toCollection(TreeSet::new)); System.out.println(“将学生姓名累积成TreeSet集合:” + snameTree.getClass()); System.out.println(snameTree); System.out.println("—————————————–\n"); // Convert elements to strings and concatenate them, separated by commas 将学生姓名累积成一个Json串 以逗号分隔 String joinedStudents = students.stream() .map(Student::toString).collect(Collectors.joining(",")); System.out.println(" 将学生姓名累积成一个Json串 以逗号分隔:" + joinedStudents); System.out.println("—————————————–\n"); // Compute sum of salaries of students 求学生总薪水 double totalSalary = students.stream() .mapToDouble(Student::getSalary).sum(); System.out.println(“学生总薪水:” + totalSalary); System.out.println("—————————————–\n"); // the min id of students 打印最小id的学生信息 System.out.println(“最小id的学生信息:”); students.stream() .min(Comparator.comparingInt(Student::getId)) .ifPresent(System.out::println); System.out.println("—————————————–\n"); // the max id of students 打印最大id的学生信息 System.out.println(“最大id的学生信息:”); students.stream() .max(Comparator.comparingInt(Student::getId)) .ifPresent(System.out::println); System.out.println("—————————————–\n"); // Compute avg of Age of students 求学生平均年龄 Double avgAge = students.stream() .collect(Collectors.averagingInt(Student::getAge)); System.out.println(“学生平均年龄:” + avgAge); System.out.println("—————————————–\n"); // Compute SummaryStatistics of Age of students 打印学生年龄的汇总信息 IntSummaryStatistics ageSummaryStatistics = students.stream() .collect(Collectors.summarizingInt(Student::getAge)); System.out.println(“学生年龄的汇总信息:” + ageSummaryStatistics); System.out.println("—————————————–\n"); // 根据性别分组 取id最小的学生 // 直接使用Collectors.minBy返回的是Optional<Student> // 因能确认不为Null 使用Collectors.collectingAndThen–>Optional::get获取 Map<String, Student> minIdStudent = students.stream(). collect(Collectors.groupingBy(Student::getSex, Collectors.collectingAndThen (Collectors.minBy(Comparator.comparingInt(Student::getId)), Optional::get))); System.out.println(minIdStudent); System.out.println("—————————————–\n"); }}九、测试结果 . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / ` | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-20 16:11:56.217 INFO 17260 — [ main] c.j.design.java8.Stream.CollectorDetail : Starting CollectorDetail on DESKTOP-87RMBG4 with PID 17260 (started by 46250 in E:\IdeaProjects\design)2019-02-20 16:11:56.223 INFO 17260 — [ main] c.j.design.java8.Stream.CollectorDetail : No active profile set, falling back to default profiles: default2019-02-20 16:11:56.699 INFO 17260 — [ main] c.j.design.java8.Stream.CollectorDetail : Started CollectorDetail in 0.678 seconds (JVM running for 1.401)—————————————–将学生姓名累积成ArrayList集合:class java.util.ArrayList[Kirito, Asuna, Sinon, Yuuki, Alice]—————————————–将学生姓名累积成TreeSet集合:class java.util.TreeSet[Alice, Asuna, Kirito, Sinon, Yuuki]—————————————– 将学生姓名累积成一个Json串 以逗号分隔:Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8),Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8),Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8),Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8),Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)—————————————–学生总薪水:4.999999995E9—————————————–最小id的学生信息:Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)—————————————–最大id的学生信息:Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)—————————————–学生平均年龄:16.0—————————————–学生年龄的汇总信息:IntSummaryStatistics{count=5, sum=80, min=14, average=16.000000, max=18}—————————————–{Female=Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8), Male=Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)}—————————————– ...

February 20, 2019 · 7 min · jiezi

深入理解 lambda表达式 与 MethodReference(四)

package com.java.design.java8.MethodReference;import com.java.design.java8.entity.Student;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.Arrays;import java.util.List;import java.util.function.Supplier;/** * @author 陈杨 */@RunWith(SpringRunner.class)@SpringBootTestpublic class MethodReference {一、测试数据准备private List<Student> students;private List<String> snames;private Student studentSupplier(Supplier<Student> studentSupplier) { return studentSupplier.get();}// private StudentConstructor studentConstructor =// (id, name, sex, age, addr, salary) ->// new Student(id, name, sex, age, addr, salary);private StudentConstructor studentConstructor = Student::new;private Student studentAllArgs(Integer id, String name, String sex, Integer age, String addr, Double salary) { return studentConstructor.studentAllArgs(id, name, sex, age, addr, salary);}@Beforepublic void init() { Student kirito = new Student(1, “Kirito”, “Male”, 18, “Sword Art Online”, 999999999.0); Student Asuna = new Student(2, “Asuna”, “Female”, 17, “Sword Art Online”, 999999999.0); Student Sinon = new Student(3, “Sinon”, “Female”, 16, “Gun Gale Online”, 999999999.0); Student Yuuki = new Student(4, “Yuuki”, “Female”, 15, “Alfheim Online”, 999999999.0); Student Alice = new Student(5, “Alice”, “Female”, 14, “Alicization”, 999999999.0); students = Arrays.asList(kirito, Asuna, Sinon, Yuuki, Alice); snames = Arrays.asList(“kirito”, “Asuna”, “Sinon”, “Yuuki”, “Alice”);}二、方法引用 引入@Testpublic void testMethodReference() { // MethodReference 方法引用 List<String> Iloveyou = Arrays.asList(“Kirito”, “Love”, “Asuna”); //集合遍历 Lambda System.out.println("—————————————\n"); System.out.println(“集合遍历 Lambda”); Iloveyou.forEach(str -> System.out.println(str)); //集合遍历 MethodReference System.out.println("—————————————\n"); System.out.println(“集合遍历 MethodReference”); Iloveyou.forEach(System.out::println);三、什么是方法引用// MethodReference// 方法引用是Lambda表达式的特殊替换// 方法引用本质是一个 函数指针 Function Pointer// 这个指针指向被引用方法// eg: 方法引用System.out::println 指向System.out.println()这个函数四、方法引用的分类1、 静态方法引用// 1、 静态方法引用// 静态方法引用 类名::静态方法名// 静态方法引用 功能实现等价于 调用 类的静态方法// 静态方法引用 与 调用 无任何关系// 类名.静态方法名 –>方法调用 显示传参// 类名::静态方法名 –>方法引用 隐式传参 编译器自动推断 方法引用的表达式 函数指针 指向 被引用函数System.out.println("—————————————\n");System.out.println(" 静态方法引用 按年龄排序");students.sort(StaticStudentComparator::staticCompareStudentByAge);students.forEach(System.out::println);System.out.println("—————————————\n");System.out.println(" 静态方法引用 按姓名排序");students.sort(StaticStudentComparator::staticCompareStudentByName);students.forEach(System.out::println);2、 对象实例方法引用// 2、 对象实例方法引用// 对象实例方法引用 引用名(对象名)::实例方法名// 对象实例方法引用 功能实现等价于 调用 对象实例 所拥有的 实例方法StudentComparator studentComparator = new StudentComparator();System.out.println("—————————————\n");System.out.println(" 静态方法引用 按年龄排序");students.sort(studentComparator::compareStudentByAge);students.forEach(System.out::println);System.out.println("—————————————\n");System.out.println(" 对象实例方法引用 按姓名排序");students.sort(studentComparator::compareStudentByName);students.forEach(System.out::println);3、 类实例方法引用// 3、 类实例方法引用// 类实例方法引用 类名::实例方法名System.out.println("—————————————\n");System.out.println(" 类实例方法引用 按年龄排序");students.sort(Student::classCompareStudentByAge);students.forEach(System.out::println);System.out.println("—————————————\n");System.out.println(" 类实例方法引用 按姓名排序");students.sort(Student::classCompareStudentByName);students.forEach(System.out::println);System.out.println("—————————————\n");System.out.println(" 类实例方法引用 容易理解的 字符串排序");snames.sort(String::compareToIgnoreCase);snames.forEach(System.out::println);4、 构造方法引用// 4、 构造方法引用// 构造方法引用 类名::new// 注意: 实体类Student 事先 有定义好的 全参构造方法 与无参构造方法// 若没有构造方法 需要自行添加 否则报错// Student::new 可以根据参数不同 对构造方法进行自动识别 重载// 利用无参构造方法构造studentNoArgs对象System.out.println("—————————————\n");System.out.println(“利用无参构造方法构造studentNoArgs对象”);Student studentNoArgs = this.studentSupplier(Student::new);System.out.println(studentNoArgs);// 利用自定义全参构造方法构造student对象System.out.println("—————————————\n");System.out.println(“利用全参构造方法构造studentNoArgs对象”);Student Silica = this.studentAllArgs (6, “Silica”, “Female”, 10, “Sword Art Online”, 999999999.0);System.out.println(Silica); }}五、StaticStudentComparator类 (静态方法实例引用)import com.java.design.java8.entity.Student;import java.util.Comparator;public class StaticStudentComparator { static Comparator<Student> studentAgeComparator = (first, last) -> first.getAge() - last.getAge(); static Comparator<Student> studentNameComparator = (first, last) -> first.getName().compareToIgnoreCase(last.getName()); public static int staticCompareStudentByAge(Student first, Student last) { return studentAgeComparator.compare(first, last); } public static int staticCompareStudentByName(Student first, Student last) { return studentNameComparator.compare(first, last); }}六、StudentComparator类 (对象方法实例引用)import com.java.design.java8.entity.Student;import java.util.Comparator;public class StudentComparator { Comparator<Student> studentAgeComparator = (first, last) -> first.getAge() - last.getAge(); Comparator<Student> studentNameComparator = (first, last) -> first.getName().compareToIgnoreCase(last.getName()); public int compareStudentByAge(Student first, Student last) { return studentAgeComparator.compare(first, last); } public int compareStudentByName(Student first, Student last) { return studentNameComparator.compare(first, last); }}七、StudentConstructor @FunctionalInterface接口 (构造方法实例引用)import com.java.design.java8.entity.Student;@FunctionalInterfacepublic interface StudentConstructor { Student studentAllArgs(Integer id, String name, String sex, Integer age, String addr, Double salary);}八 、Student实体类 (类实例方法引用)@Data@AllArgsConstructor@NoArgsConstructorpublic class Student { private Integer id; private String name; private String sex; private Integer age; private String addr; private Double salary; public int classCompareStudentByAge(Student student) { return this.getAge() - student.getAge(); } public int classCompareStudentByName(Student student) { return this.getName().compareToIgnoreCase(student.getName()); }}九、 测试结果 . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / ` | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-02 17:04:20.851 INFO 16876 — [ main] c.j.d.j.MethodReference.MethodReference : Starting MethodReference on DESKTOP-87RMBG4 with PID 16876 (started by 46250 in E:\IdeaProjects\design)2019-02-02 17:04:20.852 INFO 16876 — [ main] c.j.d.j.MethodReference.MethodReference : No active profile set, falling back to default profiles: default2019-02-02 17:04:21.422 INFO 16876 — [ main] c.j.d.j.MethodReference.MethodReference : Started MethodReference in 0.878 seconds (JVM running for 1.682)—————————————集合遍历 LambdaKiritoLoveAsuna—————————————集合遍历 MethodReferenceKiritoLoveAsuna————————————— 静态方法引用 按年龄排序Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8)Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8)Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)————————————— 静态方法引用 按姓名排序Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8)Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8)————————————— 静态方法引用 按年龄排序Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8)Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8)Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)————————————— 对象实例方法引用 按姓名排序Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8)Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8)————————————— 类实例方法引用 按年龄排序Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8)Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8)Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)————————————— 类实例方法引用 按姓名排序Student(id=5, name=Alice, sex=Female, age=14, addr=Alicization, salary=9.99999999E8)Student(id=2, name=Asuna, sex=Female, age=17, addr=Sword Art Online, salary=9.99999999E8)Student(id=1, name=Kirito, sex=Male, age=18, addr=Sword Art Online, salary=9.99999999E8)Student(id=3, name=Sinon, sex=Female, age=16, addr=Gun Gale Online, salary=9.99999999E8)Student(id=4, name=Yuuki, sex=Female, age=15, addr=Alfheim Online, salary=9.99999999E8)————————————— 类实例方法引用 容易理解的 字符串排序AliceAsunakiritoSinonYuuki—————————————利用无参构造方法构造studentNoArgs对象Student(id=null, name=null, sex=null, age=null, addr=null, salary=null)—————————————利用全参构造方法构造studentNoArgs对象Student(id=6, name=Silica, sex=Female, age=10, addr=Sword Art Online, salary=9.99999999E8) ...

February 2, 2019 · 4 min · jiezi

深入理解 lambda表达式 与 Optional Null 源码解析(Java11 三)

import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.*;import java.util.function.Function;import java.util.function.Supplier;import java.util.stream.Collectors;import java.util.stream.Stream;/** * @author 陈杨 /@RunWith(SpringRunner.class)@SpringBootTestpublic class OptionalTest { @Test public void testOptional() {一、Optional出现的缘由/ * A container object which may or may not contain a non-{@code null} value. * * 一个装泛型为T的 值容器 可以包含Null 以规避 空指针异常 * public final class Optional<T> /二、深入理解 Value-based Classes/ * Value-based Classes * * https://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html * * final immutable (但里面可以包含指向 可变对象的引用) * 具有equals、hashCode和toString的实现 仅从实例的状态计算 而不是从其标识或任何其他对象或变量的状态计算 * 不使用身份敏感的操作,例如实例之间的引用相等(==) 实例的hashCode,或实例的内部锁(intrinsic lock)同步 * 判断是否相等 仅比较equals()方法 而非对象的引用(==) * 没有可访问的构造函数 通过工厂方法实例化,不保证实例创建的一致性(不一定是单例) * * 基于值的对象 没有public的构造方法 比较值是否相等(不比较引用) /三、Optional容器的构造// private static final Optional<?> EMPTY = new Optional<>();// private final T value;// Constructs an empty instance.// private Optional() { this.value = null; }/ * Constructs an instance with the described value. * * private Optional(T value) { this.value = Objects.requireNonNull(value); } // * Returns an empty {@code Optional} instance. No value is present for this {@code Optional}. * * public static<T> Optional<T> empty() { * @SuppressWarnings(“unchecked”) * Optional<T> t = (Optional<T>) EMPTY; * return t; * } /Optional<List<String>> empty = Optional.empty();/ 构造一个容器里不为 null 的容器对象 * * public static <T> Optional<T> of(T value) { return new Optional<>(value); } /Optional<List<String>> optional = Optional.of(Arrays.asList(“Kirito”, “Love”, “Asuna”));/ 构造一个容器里可能为 null 的容器对象 * * public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); } /四、普通方法/ 获取容器中的所有值 * public T get() { * if (value == null) { * throw new NoSuchElementException(“No value present”); * } * return value; * } /System.out.println("—————————————\n");System.out.println(“optional容器中存在的值:” + optional.get());// 判断 容器中存在值 返回true// public boolean isPresent() { return value != null; }System.out.println("—————————————\n");System.out.println(“optional容器中存在值:” + optional.isPresent());System.out.println(“empty容器中存在值:” + empty.isPresent());// 判断 容器中不存在值 返回true// @since 11// public boolean isEmpty() { return value == null; }System.out.println("—————————————\n");System.out.println(“optional容器中不存在值:” + optional.isEmpty());System.out.println(“empty容器中不存在值:” + empty.isEmpty());五、高级拓展1、ifPresent(Consumer)/ * 如果存在 value 对 value 进行一个 Consumer消费 * public void ifPresent(Consumer<? super T> action) { * if (value != null) { * action.accept(value); * } * } * /System.out.println("—————————————\n");System.out.println(“optional容器中存在值就进行Consumer消费(打印输出)”);optional.ifPresent(System.out::println);2、ifPresentOrElse(Consumer)/ 如果存在 value 对 value 进行一个 Consumer消费 不存在 执行emptyAction(empty-based action) * @since 9 * public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) { * if (value != null) { * action.accept(value); * } else { * emptyAction.run(); * } * } /System.out.println("—————————————\n");System.out.println(“容器中存在值就打印值,不存在打印 hello world”);optional.ifPresentOrElse(System.out::println, () -> System.out.println(“hello world”));empty.ifPresentOrElse(System.out::println, () -> System.out.println(“hello world”));3、filter(Predicate)/ * 如果存在value 且符合预期predicate 则对 value 进行预期操作predicate.test(value) 否则返回empty * public Optional<T> filter(Predicate<? super T> predicate) { * Objects.requireNonNull(predicate); * if (!isPresent()) { * return this; * } else { * return predicate.test(value) ? this : empty(); * } * } /System.out.println("—————————————\n");System.out.println(“遍历集合元素”);optional.filter(strings -> { strings.forEach(System.out::println); return true;});4、or(Supplier)/ * 如果存在value 则返回value 否则使用supplier接口的get()方法 构造出一个Optional * @since 9 * public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) { * Objects.requireNonNull(supplier); * if (isPresent()) { * return this; * } else { * @SuppressWarnings(“unchecked”) * Optional<T> r = (Optional<T>) supplier.get(); * return Objects.requireNonNull(r); * } * } /System.out.println("—————————————\n");Supplier<Optional<List<String>>> sup = () -> Optional.ofNullable(Arrays.asList(“Optional”, “or”, “supplier”));System.out.println(empty.or(sup));5、Stream.of(value)/ * 如果存在value 则返回Stream.of(value) 否则返回一个Stream.empty() * @since 9 * public Stream<T> stream() { * if (!isPresent()) { * return Stream.empty(); * } else { * return Stream.of(value); * } * } /System.out.println("—————————————\n");System.out.println(“以stream流 遍历optional容器内的值”);Stream<List<String>> stream = Stream.of(optional.get());stream.forEach(System.out::println);6、orElse(T other)/ * 如果存在value 则返回value 否则返回T other * public T orElse(T other) { * return value != null ? value : other; * } /System.out.println("—————————————\n");System.out.println(" 容器中存在值就返回值 不存在就返回{"hello","world"}");System.out.println(empty.orElse(Arrays.asList(“hello”, “world”)));7、orElseGet(Supplier)/ * 如果存在value 则返回value 否则返回Supplier接口实现 * public T orElseGet(Supplier<? extends T> supplier) { * return value != null ? value : supplier.get(); * } /System.out.println("—————————————\n");Supplier<List<String>> listSupplier = () -> Arrays.asList(“do”, “orElseGet”);System.out.println(empty.orElseGet(listSupplier));8、orElseThrow/ * 如果存在value 则返回value 否则抛出异常NoSuchElementException * @since 10 * public T orElseThrow() { * if (value == null) { * throw new NoSuchElementException(“No value present”); * } * return value; * } /System.out.println("—————————————\n");System.out.println(" 容器中存在值就返回值 不存在就返回NoSuchElementException");System.out.println(optional.orElseThrow());try { System.out.println(empty.orElseThrow());} catch (NoSuchElementException e) { System.out.println(" NoSuchElementException —> No value present");}9、orElseThrow(Supplier)/ * 如果存在value 则返回value 否则使用Supplier接口生成一个被抛出的exceptionSupplier –>exception * public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { * if (value != null) { * return value; * } else { * throw exceptionSupplier.get(); * } * } /System.out.println("—————————————\n");Supplier<NoSuchElementException> noSuchElementException = NoSuchElementException::new;try { System.out.println(empty.orElseThrow(noSuchElementException));} catch (NoSuchElementException e) { System.out.println(" Supplier NoSuchElementException —> No value present");}10、map(Function)/ * * 如果存在value 则返回value并作为mapper的输入 将其输出作为新的value存放至Optional 否则返回一个null的Optional * 如果经过mapper 后得到的结果为null 返回一个null的Optional * mapper函数若为null 则抛出NullPointerException * map:对集合中每个元素进行操作 * * * public <U> Optional<U> map(Function<? super T, ? extends U> mapper) { * Objects.requireNonNull(mapper); * if (!isPresent()) { * return empty(); * } else { * return Optional.ofNullable(mapper.apply(value)); * } * } /System.out.println("—————————————\n");Function<List<String>, List<String>> function = up -> up.stream().map(String::toUpperCase).collect(Collectors.toList());Optional<List<String>> o = optional.map(function);System.out.println(o.get());11、flatMap(Function) / * 如果存在value 则返回value并作为mapper的输入 将其输出作为新的value存放至Optional 否则返回一个null的Optional * 如果经过mapper 后得到的结果为null 返回一个null的Optional * mapper函数若为null 则抛出NullPointerException * * 与map的方法的区别: * map * * return Optional.ofNullable(mapper.apply(value)); * * flatMap * * Optional<U> r = (Optional<U>) mapper.apply(value); * return Objects.requireNonNull(r); * flatMap:对集合中每个元素进行操作然后再扁平化 * * * public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) { * Objects.requireNonNull(mapper); * if (!isPresent()) { * return empty(); * } else { * @SuppressWarnings(“unchecked”) * Optional<U> r = (Optional<U>) mapper.apply(value); * return Objects.requireNonNull(r); * } * } */ System.out.println("—————————————\n"); Function<List<String>, Optional<List<String>>> func = up -> Optional.of(up.stream().map(String::toUpperCase).collect(Collectors.toList())); Optional<List<String>> u = optional.flatMap(func); System.out.println(u.get()); }}六、测试 . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / ` | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-02-01 18:29:34.870 INFO 17140 — [ main] com.java.design.java8.OptionalTest : Starting OptionalTest on DESKTOP-87RMBG4 with PID 17140 (started by 46250 in E:\IdeaProjects\design)2019-02-01 18:29:34.871 INFO 17140 — [ main] com.java.design.java8.OptionalTest : No active profile set, falling back to default profiles: default2019-02-01 18:29:35.437 INFO 17140 — [ main] com.java.design.java8.OptionalTest : Started OptionalTest in 0.775 seconds (JVM running for 1.574)—————————————optional容器中存在的值:[Kirito, Love, Asuna]—————————————optional容器中存在值:trueempty容器中存在值:false—————————————optional容器中不存在值:falseempty容器中不存在值:true—————————————optional容器中存在值就进行Consumer消费(打印输出)[Kirito, Love, Asuna]—————————————容器中存在值就打印值,不存在打印 hello world[Kirito, Love, Asuna]hello world—————————————遍历集合元素KiritoLoveAsuna—————————————Optional[[Optional, or, supplier]]—————————————以stream流 遍历optional容器内的值[Kirito, Love, Asuna]————————————— 容器中存在值就返回值 不存在就返回{“hello”,“world”}[hello, world]—————————————[do, orElseGet]————————————— 容器中存在值就返回值 不存在就返回NoSuchElementException[Kirito, Love, Asuna] NoSuchElementException —> No value present————————————— Supplier NoSuchElementException —> No value present—————————————[KIRITO, LOVE, ASUNA]—————————————[KIRITO, LOVE, ASUNA]Process finished with exit code 0 ...

February 1, 2019 · 5 min · jiezi

深入理解 lambda表达式 与 函数式编程 函数式接口源码解析(二)

package com.java.design.java8;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.Arrays;import java.util.Comparator;import java.util.List;import java.util.function.*;import java.util.stream.Collectors;/** * @author 陈杨 /@RunWith(SpringRunner.class)@SpringBootTestpublic class FuncInterface {一、函数式编程的理解// 函数式编程的理解//// 函数接口式编程 是 对 业务应用的进一步抽象// 在类方法定义中 只需实现FunctionalInterface 而不管业务实现的逻辑// 在外部应用 调用该业务时 使用Lambda表达式 灵活实现其业务逻辑二、 函数式接口的测试方法1、Function接口// FunctionFunction<Integer, Integer> sum = integer -> integer + 1;Function<Integer, Integer> multiply = integer -> integer * integer;List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);public int testFunctionCompose(Integer integer) { return sum.compose(multiply).apply(integer);}public int testFunctionAndThen(Integer integer) { return sum.andThen(multiply).apply(integer);}2、BiFunction接口// BiFunctionBiFunction<Integer, Integer, Integer> subtract = (first, last) -> first - last;public int testBiFunctionAndThen(Integer first, Integer last) { return subtract.andThen(multiply).apply(first, last);}3、BinaryOperator接口// BinaryOperatorBinaryOperator<Integer> binaryOperator = (first, last) -> first - last;public int testBinaryOperator(Integer first, Integer last) { return binaryOperator.apply(first, last);}public String testMinBy(String first, String last, Comparator<String> comparator) { return BinaryOperator.minBy(comparator).apply(first, last);}public String testMaxBy(String first, String last, Comparator<String> comparator) { return BinaryOperator.maxBy(comparator).apply(first, last);}//比较器//比较字符串的长度Comparator<String> length = (first, last) -> first.length() - last.length();//比较字符串首字母ASCII码大小Comparator<String> asc = (first, last) -> first.charAt(0) - last.charAt(0);4、Predicate接口// Predicatepublic List<Integer> testPredicate(Predicate<Integer> predicate) { return list.stream().filter(predicate).collect(Collectors.toList());}public Predicate<String> isEqual(Object object) { return Predicate.isEqual(object);}public Predicate<Integer> notPredicate(Predicate<Integer> predicate) { return Predicate.not(predicate);}public List<Integer> testPredicateNegate(Predicate<Integer> predicate) { return list.stream().filter(predicate.negate()).collect(Collectors.toList());}public List<Integer> testPredicateAnd(Predicate<Integer> first, Predicate<Integer> last) { return list.stream().filter(first.and(last)).collect(Collectors.toList());}public List<Integer> testPredicateOr(Predicate<Integer> first, Predicate<Integer> last) { return list.stream().filter(first.or(last)).collect(Collectors.toList());}5、Supplier接口// Supplier@Data@AllArgsConstructor@NoArgsConstructorprivate class Student { private Integer id; private String name; private String sex; private Integer age; private String addr; private Double salary;}三、测试结果 . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / ` | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)2019-01-31 11:51:58.460 INFO 12080 — [ main] com.java.design.java8.FuncInterface : Starting FuncInterface on DESKTOP-87RMBG4 with PID 12080 (started by 46250 in E:\IdeaProjects\design)2019-01-31 11:51:58.461 INFO 12080 — [ main] com.java.design.java8.FuncInterface : No active profile set, falling back to default profiles: default2019-01-31 11:51:58.988 INFO 12080 — [ main] com.java.design.java8.FuncInterface : Started FuncInterface in 0.729 seconds (JVM running for 1.556)——————–Function接口的理解———————6581——————BiFunction接口的理解———————64——————-Predicate接口的理解———————获取满足条件的集合:大于4[5, 6, 7, 8, 9]——————————获取满足条件的集合:大于4且是偶数[6, 8]——————————获取满足条件的集合:大于4 取反[1, 2, 3, 4, 0]——————————获取满足条件的集合:大于4或是偶数[2, 4, 5, 6, 7, 8, 9, 0]——————————使用Objects的Equals方法判断对象是否相同true——————————Predicate.not()返回(Predicate<T>)target.negate(); [1, 2, 3, 4, 0]——————————双重否定表肯定[5, 6, 7, 8, 9]————————————————-Supplier接口的理解———————FuncInterface.Student(id=1, name=Kirito, sex=Male, age=18, addr=ShenZhen, salary=9.99999999E8)———————————————BinaryOperator接口的理解——————-继承BiFunction的Apply方法 实现减法10 - 2 = 8——————————字符串较短的是:Asuna字符串较长的是:Kirito——————————字符串首字母ASCII码较小的是:Asuna字符串首字母ASCII码较大的是:KiritoProcess finished with exit code 0四、透过现象看本质 函数式接口的源码实现1、Function接口@Testpublic void testFuncInterface() { System.out.println("——————–Function接口的理解———————\n"); // Function接口的理解 // public interface Function<T, R> // R apply(T t); // Represents a function that accepts one argument and produces a result. // 一个函数:接收一个参数 返回一个结果 // T 输入类型 R 输出类型 /default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } * Returns a composed function that first applies the {@code before} * function to its input, and then applies this function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. / // 输入–>beforeFunction()处理–>得到结果作为下一个函数apply()的输入参数 形成函数式接口的串联调用 // beforeFunction 在当前函数apply前 进行调用 /default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } * Returns a composed function that first applies this function to * its input, and then applies the {@code after} function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. / // 输入–>apply()处理–>得到结果作为下一个函数after.apply()的输入参数 形成函数式接口的串联调用 // afterFunction 在当前函数apply后 进行调用 /static <T> Function<T, T> identity() { return t -> t; * Returns a function that always returns its input argument. }/ // 总是返回输入参数 // 总结: // function1.compose(function2) 执行顺序 –>BeforeFunction()–>thisFunction()–> function2 –> function1 // function1.andThen(function2) 执行顺序 –>thisFunction()–>AfterFunction()–> function1 –> function2 // 前一个函数的运算结果 作为下一个函数的输入参数 // 理解运行时机 可以类比 Junit中 @Before 与 @After System.out.println(this.testFunctionCompose(8)); System.out.println(this.testFunctionAndThen(8));2、BiFunction接口System.out.println("——————BiFunction接口的理解———————\n");// BiFunction 接口 的 理解// @FunctionalInterface// public interface BiFunction<T, U, R> {// R apply(T t, U u);// 一个函数:接收二个参数 返回一个结果/ default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t, U u) -> after.apply(apply(t, u)); } }/// 利用反证法 可以证明 BiFunction 没有 compose方法 (提示: 参数 与 返回值)// 将2个参数应用于BiFunction 只会得到一个返回值 这个返回值会作为Function传入的参数// biFunction.andthen(function)System.out.println(this.testBiFunctionAndThen(10, 2));3、Predicate接口System.out.println("——————-Predicate接口的理解———————\n");// public interface Predicate<T> {// Represents a predicate (boolean-valued function) of one argument./ * Evaluates this predicate on the given argument. * * 接收一个判断 判断是否满足预期条件 返回true false * boolean test(T t); /System.out.println(“获取满足条件的集合:大于4”);System.out.println(this.testPredicate(in -> in > 4));System.out.println("——————————\n"); / * Returns a composed predicate that represents a short-circuiting logical * AND of this predicate and another. When evaluating the composed * predicate, if this predicate is {@code false}, then the {@code other} * predicate is not evaluated. * * 短路逻辑与计算 * default Predicate<T> and(Predicate<? super T> other) { * Objects.requireNonNull(other); * return (t) -> test(t) && other.test(t); }/System.out.println(“获取满足条件的集合:大于4且是偶数”);System.out.println(this.testPredicateAnd(in -> in > 4, in -> in % 2 == 0));System.out.println("——————————\n");/ * Returns a predicate that represents the logical negation of this * predicate. * * 取反 * default Predicate<T> negate() { * return (t) -> !test(t); * } /System.out.println(“获取满足条件的集合:大于4 取反”);System.out.println(this.testPredicateNegate(in -> in > 4));System.out.println("——————————\n");/ * Returns a composed predicate that represents a short-circuiting logical * OR of this predicate and another. When evaluating the composed * predicate, if this predicate is {@code true}, then the {@code other} * predicate is not evaluated. * * 短路逻辑或计算 * default Predicate<T> or(Predicate<? super T> other) { * Objects.requireNonNull(other); * return (t) -> test(t) || other.test(t); * } /System.out.println(“获取满足条件的集合:大于4或是偶数”);System.out.println(this.testPredicateOr(in -> in > 4, in -> in % 2 == 0));System.out.println("——————————\n");/ * Returns a predicate that tests if two arguments are equal according * to {@link Objects#equals(Object, Object)}. * * 根据Objects的equals方法 来判断两个对象 是否相同 * static <T> Predicate<T> isEqual(Object targetRef) { * return (null == targetRef) * ? Objects::isNull * : object -> targetRef.equals(object); * } /System.out.println(“使用Objects的Equals方法判断对象是否相同”);System.out.println(this.isEqual(“Kirito”).test(“Kirito”));System.out.println("——————————\n");/ * Returns a predicate that is the negation of the supplied predicate. * This is accomplished by returning result of the calling * {@code target.negate()}. * * 返回提供的predicate的否定 * @SuppressWarnings(“unchecked”) * static <T> Predicate<T> not(Predicate<? super T> target) { * Objects.requireNonNull(target); * return (Predicate<T>)target.negate(); * } * } /System.out.println(“Predicate.not()返回(Predicate<T>)target.negate(); “);System.out.println(testPredicate(this.notPredicate(integer -> integer > 4)));System.out.println(”——————————\n”);System.out.println(“双重否定表肯定”);System.out.println(testPredicateNegate(this.notPredicate(integer -> integer > 4)));System.out.println("——————————\n");4、Supplier接口System.out.println("——————-Supplier接口的理解———————\n");/ * Represents a supplier of results. * * public interface Supplier<T> { * T get(); * } /// 构造方法引用 构造函数接口实例// 利用Supplier接口 Student类必须要有无参的构造方法// Supplier<Student> studentSupplier = () -> new Student();Supplier<Student> studentSupplier = Student::new;Student student = studentSupplier.get();student.setId(1);student.setName(“Kirito”);student.setSex(“Male”);student.setAge(18);student.setSalary(999999999.0);student.setAddr(“ShenZhen”);System.out.println(student);System.out.println("——————————\n");5、BinaryOperator接口 System.out.println("—————BinaryOperator接口的理解——————-\n"); / * * public interface BinaryOperator<T> extends BiFunction<T,T,T> { * * 返回2个比较参数之间的较小值 * public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) { * Objects.requireNonNull(comparator); * return (a, b) -> comparator.compare(a, b) <= 0 ? a : b; * } * * 返回2个比较参数之间的较大值 * public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) { * Objects.requireNonNull(comparator); * return (a, b) -> comparator.compare(a, b) >= 0 ? a : b; * } * } */ System.out.println(“继承BiFunction的Apply方法 实现减法”); System.out.println(“10 - 2 = “+this.testBinaryOperator(10, 2)); System.out.println(”——————————\n”); System.out.println(“字符串较短的是:"+this.testMinBy(“Kirito”,“Asuna”,length)); System.out.println(“字符串较长的是:"+this.testMaxBy(“Kirito”,“Asuna”,length)); System.out.println(”——————————\n”); System.out.println(“字符串首字母ASCII码较小的是:"+this.testMinBy(“Kirito”,“Asuna”,asc)); System.out.println(“字符串首字母ASCII码较大的是:"+this.testMaxBy(“Kirito”,“Asuna”,asc)); }} ...

January 31, 2019 · 6 min · jiezi

深入理解lambda表达式与@FunctionalInterface函数式接口(一)

一、集合遍历与Lambda表达式 引入package com.java.design.java8;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.*;import java.util.function.Consumer;/** * @author 陈杨 */@RunWith(SpringRunner.class)@SpringBootTestpublic class ErgodicList { @Test public void testErgodicList() { // 直接构造集合对象 保证了集合size>0 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0); System.out.println("—————————传统for循环——————–\n"); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } System.out.println("—————————增强for循环——————–\n"); for (Integer i : list) { System.out.println(i); } System.out.println("—————————迭代器————————-\n"); Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { Integer integer = iterator.next(); System.out.println(integer); } System.out.println("—————————forEach————————\n"); list.forEach(new Consumer<Integer>() { @Override public void accept(Integer integer) { System.out.println(integer); } });二、 @FunctionalInterface函数式接口与Lambda表达式1、概念// Consumer @FunctionalInterface函数式接口// Conceptually, a functional interface has exactly one abstract method.// 从概念上看,一个函数式接口有且只有一个精确的抽象方法// 从java8开始 接口中不仅仅存在抽象方法 还能存在有具体实现的方法(默认方法)2、 函数式接口的区分// Since {@linkplain java.lang.reflect.Method#isDefault()// default methods} have an implementation, they are not abstract. If// an interface declares an abstract method overriding one of the// public methods of {@code java.lang.Object}, that also does// <em>not</em> count toward the interface’s abstract method count// since any implementation of the interface will have an// implementation from {@code java.lang.Object} or elsewhere.// 因为java.lang.reflect.Method#isDefault() default methods 有一个实现 所以不是抽象的// 如果一个接口声明一个抽象方法,其实现了java.lang.Object类中public方法:不计入抽象方法的个数3、函数式接口的实例化方式// Note that instances of functional interfaces can be created with// lambda expressions, method references, or constructor references.// 函数式接口的实例化: lambda表达式 方法引用 构造方法引用4、函数式接口中的默认方法// default void forEach(Consumer<? super T> action) {// Objects.requireNonNull(action);// for (T t : this) {// action.accept(t);// }// }// action 针对每个元素 执行的动作行为// default 修饰接口 已实现的默认方法5、总结与思考// 1、如果一个接口中有且只有一个抽象方法 则其为一个函数式接口// 2、如果一个接口上声明了@FunctionalInterface注解 则编译器会按照函数式接口的定义来要求该接口// If a type is annotated with this annotation type, compilers are// required to generate an error message unless:// (1)The type is an interface type and not an annotation type, enum, or class.// (2)The annotated type satisfies the requirements of a functional interface.// However, the compiler will treat any interface meeting the// definition of a functional interface as a functional interface// regardless of whether or not a {@code FunctionalInterface}// annotation is present on the interface declaration.// 3、如果接口上只有一个抽象方法,但我们没有对其加上@FunctionalInterface 编译器仍然将其看作函数式接口// 加上注解后 一目了然 如果没有满足强制性要求 则会抛出错误信息// 4、只有一个抽象方法的接口 有必要加上 @FunctionalInterface 如 Runnable接口// 5、所有的函数式接口 都可以使用lambda表达式 实现(表达易懂 简单)三、函数式接口实例化 之 Lambda表达式System.out.println("——————–lambda创建函数式接口实例—————\n");list.forEach(i -> { // 若能推断出i的类型 不需要声明 // 如不能推断出(Integer i)->{} System.out.println(i);});四、在排序过程中 Lambda表达式的 演变System.out.println("————————–lambda排序———————–\n");// Collections.sort(list, new Comparator<Integer>() {// @Override// public int compare(Integer o1, Integer o2) {// return o2.compareTo(o1);// }// });// Collections.sort(list,(String o1, String o2)->{return o2.compareTo(o1);});// Collections.sort(list, (o1, o2) -> { return o2.compareTo(o1); });// statement { return o2.compareTo(o1); }// expression o2.compareTo(o1)// Collections.sort(list,(String::compareTo));// Collections.sort(list,Collections.reverseOrder());Collections.sort(list, (o1, o2) -> o2.compareTo(o1));System.out.println(list);五、函数式接口实例化 之 方法引用System.out.println("——————–方法引用创建函数式接口实例————–\n");list.forEach(System.out::println); }}六、深入理解Lambda表达式// lambda表达式:// 1、从函数式编程角度来看:// lambda表达式为Java添加了函数式编程的新特性 函数升格成为一等公民// 在函数作为一等公民的语言 如Python中 lambda表达式为函数类型// 但在java中 lambda表达式是对象类型 依赖于函数式接口Functional Interface// 2、lambda表达式书写// lambda表达式形式 () -> {} 必需根据上下文确定其匿名函数类型 函数方法 -> 函数实现// () 省略 参数只有一个且类型可根据上下文推导// {} 省略 方法体主体只有一条语句,返回值类型与主体表达式(匿名函数)一致// 3、进一步理解lambda表达式// lambda表达式传递行为action 不仅仅是值的传递 (类比Node.js的事件驱动 与 回调函数callback)// lambda表达式替换前: 事先定义对象及所持有的方法 根据 “对象.方法” 进行方法的调用 预先定义好的action// lambda表达式替换后: {} 方法调用 R apply(T t); 事先不知道action 仅在调用时才知道 action// 提升抽象层次 API重用性 使用灵活 ...

January 30, 2019 · 2 min · jiezi