Java8实战-总结12

news2024/11/15 21:24:12

Java8实战-总结12

  • Lambda表达式
    • Lambda 和方法引用实战
      • 第1步:传递代码
      • 第2步:使用匿名类
      • 第3步:使用Lambda表达式
      • 第4步:使用方法引用
    • 复合Lambda表达式的有用方法
      • 比较器复合
        • 逆序
        • 比较器链
      • 函数复合

Lambda表达式

Lambda 和方法引用实战

为了给所有关于Lambda的内容收个尾,需要继续研究开始的那个问题——用不同的排序策略给一个Apple列表排序,并需要展示如何把一个原始粗暴的解决方案转变得更为简明。这会用到迄今讲到的所有概念和功能:行为参数化、匿名类、Lambda表达式和方法引用。想要实现的最终解决方案是这样的:

inventory.sort(comparing(Apple::getWeight));

第1步:传递代码

Java 8API已经提供了一个List可用的sort方法,不用自己去实现它。最困难的部分已经搞定了。但是,如何把排序策略传递给sort方法?sort方法的签名是这样的:

void sort (Comparator<? super E> c)

它需要一个Comparator对象来比较两个Apple。这就是在Java中传递策略的方式:它们必须包裹在一个对象里。sort的行为被参数化了:传递给它的排序策略不同,其行为也会不同。

第一个解决方案看上去是这样的:

	public class AppleComparator implements Comparator<Apple> {
		public int compare(Apple a1, Apple a2) {
			return a1.getWeight().compareTo(a2.getWeight());
		}
	}
	
	inventory.sort(new AppleComparator());

第2步:使用匿名类

在前面看到了,可以使用匿名类来改进解决方案,而不是实现一个Comparator却只实例化一次:

	inventory.sort(new Comparator<Apple>() {
		public int compare(Apple al, Apple a2) {
			return a1.getWeight().compareTo(a2.getWeight ());
		}
	});

第3步:使用Lambda表达式

但解决方案仍然挺啰嗦的。Java 8引入了Lambda表达式,它提供了一种轻量级语法来实现相同的目标:传递代码。在需要函数式接口的地方可以使用Lambda表达式。回顾一下:函数式接口就是仅仅定义一个抽象方法的接口。抽象方法的签名(称为函数描述符)描述了Lambda表达式的签名。在这个例子里,Comparator代表了函数描述符(T, T) -> int。因为你用的是苹果,所以它具体代表的就是(Apple, Apple) -> int。改进后的新解决方案看上去就是这样的:

inventory.sort((Apple a1,Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
);

前面解释过了,Java编译器可以根据Lambda出现的上下文来推断Lambda表达式参数的类型。那么解决方案就可以重写成这样:

inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));

代码还能变得更易读一点吗?Comparator具有一个叫作comparing的静态辅助方法,它可以接受一个Function来提取Comparable键值,并生成一个Comparator对象。它可以像下面这样用(注意现在传递的Lambda只有一个参数:Lambda说明了如何从苹果中提取需要比较的键值):

Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());

现在可以把代码再改得紧凑一点了:

import static java.util.Comparator.comparing;inventory.sort(comparing((a) -> a.getWeight()));

第4步:使用方法引用

前面解释过,方法引用就是替代那些转发参数的Lambda表达式的语法糖。可以用方法引用让代码更简洁(假设静态导入了java.util.Comparator.comparing):

inventory.sort(comparing(Apple::getWeight));

这就是你的最终解决方案。这比Java 8之前的代码好在哪儿呢?它比较短;它的意思也很明显,并且代码读起来和问题描述差不多:“对库存进行排序,比较苹果的重量。”

复合Lambda表达式的有用方法

Java 8的好几个函数式接口都有为方便而设计的方法。具体而言,许多函数式接口,比如用于传递Lambda表达式的Comparator、Function和Predicate都提供了允许你进行复合的方法。这是什么意思呢?在实践中,这意味着你可以把多个简单的Lambda复合成复杂的表达式。比如,你可以让两个谓词之间做一个or操作,组合成一个更大的谓词。而且,你还可以让一个函数的结果成为另一个函数的输入。你可能会想,函数式接口中怎么可能有更多的方法呢?(毕竞,这违背了函数式接口的定义啊!)窍门在于,我们即将介绍的方法都是默认方法,也就是说它们不是抽象方法。我们会在第9章详谈。现在只需相信我们,等想要进一步了解默认方法以及你可以用它做什么时,再去看看第9章。

比较器复合

前面看到,可以使用静态方法Comparator.comparing,根据提取用于比较的键值的Function来返回一个Comparator,如下所示:

Comparator<Apple> c = Comparator.comparing(Apple::getWeight);

逆序

如果想要对苹果按重量递减排序怎么办?用不着去建立另一个Comparator的实例。接口有一个默认方法reversed可以使给定的比较器逆序。因此仍然用开始的那个比较器,只要修改一下前一个例子就可以对苹果按重量递减排序:

//按重量递减排序
inventory.sort(comparing(Apple::getWeight).reversed());

比较器链

上面说得都很好,但如果发现有两个苹果一样重怎么办?哪个苹果应该排在前面呢?可能需要再提供一个Comparator来进一步定义这个比较。比如,在按重量比较两个苹果之后,可能想要按原产国排序。thenComparing方法就是做这个用的。它接受一个函数作为参数(就像comparing方法一样),如果两个对象用第一个Comparator比较之后是一样的,就提供第二个Comparator。又可以优雅地解决这个问题了:

//按重量递减排序
inventory.sort (comparing(Apple::getWeight)
		.reversed()
		.thenComparing(Apple::getCountry));
两个苹果一样重时,进一步按国家排序

##谓词复合
谓词接口包括三个方法:negateandor,可以重用已有的Predicate来创建更复杂的谓词。比如,可以使用negate方法来返回一个Predicate的非,比如苹果不是红的: 产生现有

PredicatePredicatecApple> notRedApple = redApple.negate();

对象redApple想要把两个Lambdaand方法组合起来,比如一个苹果既是红色又比较重:
链接两个谓词来生成另

Predicate<Apple> redAndHeavyApple =一个predicate对象redApple.and(a -> a.getWeight()> 150);

可以进一步组合谓词,表达要么是重(150克以上)的红苹果,要么是绿苹果:

//链接Predicate的方法来构造更复杂Predicate对象
Predicate<Apple> redAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() >  150).or(a ->"green".equals(a.getColor()));

这一点为什么很好呢?从简单Lambda表达式出发,可以构建更复杂的表达式,但读起来仍然和问题的陈述差不多!请注意,andor方法是按照在表达式链中的位置,从左向右确定优先级的。因此,a.or(b).and(c)可以看作(a || b)&& c

函数复合

最后,还可以把Function接口所代表的Lambda表达式复合起来。Function接口为此配了andThencompose两个默认方法,它们都会返回Function的一个实例。

andThen方法会返回一个函数,它先对输入应用一个给定函数,再对输出应用另一个函数。比如,假设有一个函数f给数字加1 (x -> x +1),另一个函数g给数字乘2,可以将它们组合成一个函数h,先给数字加1,再给结果乘2:

//数学上会写作g(f(x))或(g o f)(x)
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
//这将返回4
int result = h.apply(1);

也可以类似地使用compose方法,先把给定的函数用作compose的参数里面给的那个函数,然后再把函数本身用于结果。比如在上一个例子里用compose的话,它将意味着f(g(x)),而andThen则意味着g(f(x)):

Function<Integer, Integer> f = x -> x + 1;
Function<Integer,Integer> g = x -> x * 2;
//数学上会写作f(g(x))或(f o g)(x)
Function<Integer, Integer> h = f.compose(g);
//这将返回3
int result = h.apply(1);

下图说明了andThencompose之间的区别。
在这里插入图片描述
这一切听起来有点太抽象了。那么在实际中这有什么用呢?比方说有一系列工具方法,对
String表示的一封信做文本转换:

public class Letter {
	public static String addHeader(String text) {
		return "From Raoul, Mario and Alan:" + text;
	}
public static String addFooter(String text) {
	return text + "Kind regards";
	
public static String checkspelling(String text) {
	return text.replaceAll("labda","lambda");

现在以通过复合这些工具方法来创建各种转型流水线了,比如创建一个流水线:先加上抬头,然后进行拼写检查,最后加上一个落款,如下图所示:

Punction<String, String> addHeader = Letter::addHeader;
Function<String, String> transformationPipeline= addHeader.andThen(Letter::checkspelling).andThen(Letter::addFooter);

在这里插入图片描述

第二个流水线可能只加抬头、落款,而不做拼写检查:

Function<String, String> addHeader = Letter::addHeader;
Function<String, String> transformationPipeline= addHeader.andThen(Letter::addFooter);

s

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

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

相关文章

【C++】从无到有了解并掌握C++面向对象编程的三大特性——封装、继承、多态

前置知识&#xff1a;类和对象 参考书籍&#xff1a;《C Primer 第五版》 目录 什么是面向过程&#xff1f;什么是面向对象&#xff1f; 一、封装 1、封装的含义以及如何实现封装 1.1 访问限定符&#xff08;访问说明符&#xff09; 1.2 什么是封装&#xff1f; 2、封装的优点…

2023年华数杯选题人数发布!!

该选题人数&#xff0c;主要基于根据各个平台开赛后12小时各项数据统计&#xff0c;进行评估&#xff08;方法见注释&#xff09;&#xff0c;最终得出2023年华数杯选选题人数&#xff0c;大致为 题号选题人数A120B159C420 注释&#xff1a;选题人数来源&#xff1a;源自各个平…

Java字符串常量池以及new String(“abc“)到底创建了几个对象?各种字符串到底相不相等?

new String(“abc”)到底创建了几个对象&#xff1f; 字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串&#xff08;String 类&#xff09;专门开辟的一块区域&#xff0c;主要目的是为了避免字符串的重复创建。 1.如果字符串常量池中不存在“abc”的引用&#xff…

GD32F103VE外部中断

GD32F103VE外部中断线线0~15&#xff0c;对应外部IO口的输入中断。它有7个中断向量&#xff0c;外部中断线0 ~ 4分别对应EXTI0_IRQn ~ EXTI4_IRQn中断向量&#xff1b;外部中断线 5 ~ 9 共用一个 EXTI9_5_IRQn中断向量&#xff1b;外部中断线10~15 共用一个 EXTI15_10_IRQn中断…

深入探索Vue.js核心技术与跨平台开发uni-app实战

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 前言 在当今Web应用不断…

小程序商城免费搭建之java商城 电子商务Spring Cloud+Spring Boot+二次开发+mybatis+MQ+VR全景+b2b2c bbc

​ 1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前…

7、Kubernetes核心技术 - Secret

目录 一、Secret概述 二、Secret 三种类型 2.1、Opaque 2..2、kubernetes.io/dockerconfigjson 2.3、kubernetes.io/service-account-token 三、Secret创建 3.1、命令行方式创建 Secret 3.2、yaml方式创建 Secret 四、Secret解码 五、Secret使用 5.1、将 Secret 挂载…

Mybatis实现JsonObject对象与JSON之间交互

项目中使用PostGresql数据库进行数据存储&#xff0c;表中某字段为Json类型&#xff0c;用于存储Json格式数据。PG数据库能够直接存储Json算是一大特色&#xff0c;很多特定情境下使用直接存储Json字段数据能够大量节省开发时间&#xff0c;提高后台数据查询和转换效率。 1、基…

第三章 图论 No.1单源最短路及其综合应用

文章目录 1129. 热浪1128. 信使1127. 香甜的黄油1126. 最小花费920. 最优乘车903. 昂贵的聘礼1135. 新年好340. 通信线路342. 道路与航线341. 最优贸易 做乘法的最短路时&#xff0c;若权值>0&#xff0c;只能用spfa来做&#xff0c;相等于加法中的负权边 1129. 热浪 1129.…

Spring接口ApplicationRunner的作用和使用介绍

在Spring框架中&#xff0c;ApplicationRunner接口是org.springframework.boot.ApplicationRunner接口的一部分。它是Spring Boot中用于在Spring应用程序启动完成后执行特定任务的接口。ApplicationRunner的作用是在Spring应用程序完全启动后&#xff0c;执行一些初始化任务或处…

W6100-EVB-PICO做DNS Client进行域名解析(四)

前言 在上一章节中我们用W6100-EVB-PICO通过dhcp获取ip地址&#xff08;网关&#xff0c;子网掩码&#xff0c;dns服务器&#xff09;等信息&#xff0c;给我们的开发板配置网络信息&#xff0c;成功的接入网络中&#xff0c;那么本章将教大家如何让我们的开发板进行DNS域名解…

python二级用哪个版本的软件,二级python用哪个版本

大家好&#xff0c;给大家分享一下python二级用哪个版本的软件&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; Python开发软件可根据其用途不同分为两种&#xff0c;一种是Python代码编辑器&#xff0c;一种是Python集成开发工具&#xff…

Mac显示隐藏文件夹

1、设置隐藏文件可见 defaults write com.apple.finder AppleShowAllFiles TRUE 2、killall Finder killall Finder

H3C交换机如何清除接口配置信息

环境&#xff1a; H3C S6520-26Q-SI 问题描述&#xff1a; H3C交换机如何清除接口配置信息 解决方案&#xff1a; 在接口下使用default命令 [H3C-Ten-GigabitEthernet1/0/3]dis th # interface Ten-GigabitEthernet1/0/3port link-mode bridgeport link-type trunkport tr…

linux的搭建以及网关配置

Linux 目前国内 Linux 更多的是应用于服务器上,而桌面操作系统更多使用的是 Windows 安装linux虚拟机 网址 VMware下载网址 点击Workstation 16 Pro for Windows下载 安装CentOS centO下载网址 依次点击 7.6.1810/ isos/ x86_64/ CentOS-7-x86_64-DVD-1810.…

面试热题(前中序遍历构建树)

给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 题目中是给定两个数组&#xff0c;一个是存放这颗树的前序遍历的数组&#xff0c;一个是存放这棵树的…

一个严肃的话题,ADR会取代WAF和RASP吗?

做安全的人应该都对WAF耳熟能详&#xff0c;也就是我们常说的Web应用防火墙&#xff0c;成为了应用安全防护的明星产品之一。从传统的防火墙、IDS、IPS&#xff0c;再到WAF横空出世&#xff0c;引领技术趋势若干年&#xff0c;这一阶段可以称为应用安全防护1.0时代。作为一款成…

通过华为杯竞赛、高教社杯和数学建模国赛实现逆袭;助力名利双收

文章目录 ⭐ 赛事介绍⭐ 参赛好处⭐ 辅导比赛⭐ 写在最后 ⭐ 赛事介绍 华为杯全国研究生数学建模竞赛是由华为公司主办的一项面向全国研究生的数学建模竞赛。该竞赛旨在通过实际问题的建模和解决&#xff0c;培养研究生的创新能力和团队合作精神&#xff0c;推动科技创新和应用…

深度解析线程池的文章

java 系统的运行归根到底是程序的运行&#xff0c;程序的运行归根到底是代码的执行&#xff0c;代码的执行归根到底是虚拟机的执行&#xff0c;虚拟机的执行其实就是操作系统的线程在执行&#xff0c;并且会占用一定的系统资源&#xff0c;如CPU、内存、磁盘、网络等等。所以&a…

IPsec简介

IPsec简介 IPsec&#xff08;IP Security&#xff09;是IETF制定的三层隧道加密协议&#xff0c;它为Internet上传输的数据提供了高质量的、可互操作的、基于密码学的安全保证。特定的通信方之间在IP层通过加密与数据源认证等方式&#xff0c;提供了以下的安全服务&#xff1a…