type
Post
status
Published
date
Sep 13, 2023
slug
summary
tags
category
学习思考
icon
password
1 什么是Lambda(λ)2 不同语言中的Lambda3 Java中的Lambda表达式4 原生函数式接口4.1 @FunctionalInterface注解4.2 Consumer: 消费性接口4.3 Supplier: 供给型接口4.4 Function: 函数型接口4.5 Predicate: 断言型接口5 Stream表达式5.1 中间型操作5.2 终结型操作5.3 Stream的创建5.4 Stream的使用5.4.1 遍历/匹配(foreach/find/match)5.4.2 筛选(filter)5.4.3 聚合(max/min/count)5.4.4 映射(map/flatMap)5.4.5 归约(reduce)5.4.6 收集(collect)5.4.6.1 归集(toList/toSet/toMap)5.4.6.2 统计(count/averaging)5.4.6.3 分组(partitioningBy/groupingBy)5.4.6.4 接合(joining)5.4.6.5 归约(reducing)5.4.7 排序(sorted)5.4.8 提取/组合6 引用
1 什么是Lambda(λ)
Lambda 并不是一个什么的缩写,它是希腊第十一个字母 λ 的读音,同时它也是微积分函数中的一个概念,所表达的意思是一个函数入参和出参定义,在编程语言中其实是借用了数学中的 λ,并且多了一点含义,在编程语言中功能代表它具体功能的叫法是匿名函数(Anonymous Function)。
百度百科:
匿名函数(英语:Anonymous Function)在计算机编程中是指一类无需定义标识符(函数名)的函数或子程序。
Lambda 的历史,它在 JDK8 发布之后才正式出现,但是在编程语言界,它是一个具有悠久历史的东西,最早在 1958 年在Lisp 语言中首先采用,而且虽然Java脱胎于C++,但是C++在2011年已经发布了Lambda 了,但是 JDK8 的 LTS 在2014年才发布,现代编程语言则是全部一出生就自带 Lambda 支持。
Lambda 在编程语言中往往是一个匿名函数,也就是说Lambda 是一个抽象概念,而编程语言提供了配套支持,比如在 Java 中其实为Lambda 进行配套的就是函数式接口,通过函数式接口生成匿名类和方法进行Lambda 式的处理。
Lambda 所提供的好处在Java中就是函数式接口所提供的能力了,函数式接口往往则是提供了一些通用能力,这些函数式接口在JDK中也有一套完整的实践,那就是 Stream。
2 不同语言中的Lambda
Python
C++

[1]:Lambda表达式的引入标志,在‘[]’里面可以填入‘=’或‘&’表示该lambda表达式“捕获”(lambda表达式在一定的scope可以访问的数据)的数据时以什么方式捕获的,‘&’表示一引用的方式;‘=’表明以值传递的方式捕获,除非专门指出。
[2]:Lambda表达式的参数列表
[3]:Mutable 标识
[4]:异常标识
[5]:返回值
[6]:“函数”体,也就是lambda表达式需要进行的实际操作。
JavaScript
3 Java中的Lambda表达式
Lambda 表达式在 Java 8 中添加的。
lambda 表达式是一小段代码,它接受参数并返回一个值。Lambda 表达式类似于方法,但它们不需要名称,并且可以直接在方法体中实现。
句法
最简单的 lambda 表达式包含一个参数和一个表达式:
0参数
1个参数
多个参数
上面的表达式有一定的限制。它们要么返回一个值要么执行一段方法,并且它们不能包含变量、赋值或语句,例如if or for 。为了进行更复杂的操作,可以使用带有花括号的代码块。如果 lambda 表达式需要返回一个值,那么代码块应该有一个return语句。
句法
• 类 :: 静态方法
• 对象 :: 实例方法
• 构造器 :: new
4 原生函数式接口
4.1 @FunctionalInterface注解
有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,可以使用Lambda表达式创建一个函数式接口的对象,一定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。
与@Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:
@FunctionalInterface 。该注解可用于一个接口的定义上,一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法(equal和hashcode方法不算),否则将会报错。但是这个注解不是必须的,只要符合函数式接口的定义,那么这个接口就是函数式接口。4.2 Consumer: 消费性接口
Consumer通过名字可以看出它是一个消费函数式接口,主要针对的是消费(1..n 入参, 无返回)这个场景,它的代码定义如下:
通过泛型 T 定义了一个入参,但是没有返回值,它代表你可以针对这个入参做一些自定义逻辑,比较典型的例子是 forEach 方法。
Demo
4.3 Supplier: 供给型接口
Supplier通过名字比较难看出来它是一个场景的函数式接口,它主要针对的是说获取(无入参,有返回)这个场景,它的代码定义如下:
通过泛型 T 定义了一个返回值类型,但是没有入参,它代表你可以针对调用方获取某个值,比较典型的例子是 Stream 中的 collect 方法,通过自定义传入我们想要取得的某种对象进行对象收集。
Demo
4.4 Function: 函数型接口
Function 接口的名字不太能轻易看出来它的场景,它主要针对的则是 转换(有入参,有返回,其中T是入参,R是返回)这个场景,其实说转换可能也不太正确,它是一个覆盖范围比较广的场景,你也可以理解为扩展版的Consumer,接口定义如下:
通过一个入参 T 进行自定义逻辑处理,最终得到一个出参 R,比较典型的例子是 Stream 中的 map 系列方法和 reduce 系列方法。
Demo
4.5 Predicate: 断言型接口
Predicate主要针对的是判断(有入参,有返回,凡是返回的类型固定为Boolean。可以说Function 是包含Predicate的 )这个场景,它的代码定义如下:
通过泛型 T 定义了一个入参,返回了一个布尔值,它代表你可以传入一段判断逻辑的函数,比较典型的例子是 Stream 中的 filter方法。
5 Stream表达式
Stream,就是JDK8又依托于函数式编程特性为集合类库做的一个类库,它其实就是jdk提供的函数式接口的最佳实践。它能让我们通过lambda表达式更简明扼要的以流水线的方式去处理集合内的数据,可以很轻松的完成诸如:过滤、分组、收集、归约这类操作。
其中Stream的操作大致分为两类
- 中间型操作
- 终结型操作
其中转换型操作又分为有状态和无状态两类。有状态是本次的结果需要依赖于前面的处理结果,而无状态则是不依赖。简单来讲就是无状态方法可以互相调换位置,而有状态方法不能调换位置。
注意:
- stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
- stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
- stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
5.1 中间型操作
中间型操作就是返回值依旧是stream类型的方法。api如下:

5.2 终结型操作
终结型操作与中间型相反,返回值是非Stream类型的。api如下:

5.3 Stream的创建
通过
java.util.Collection.stream() 方法用集合创建流使用
java.util.Arrays.stream(T[] array)方法用数组创建流使用
Stream的静态方法:of()、iterate()、generate()5.4 Stream的使用
什么是Optional?
Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
5.4.1 遍历/匹配(foreach/find/match)
Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。
5.4.2 筛选(filter)
按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。

5.4.3 聚合(max/min/count)
max、min、count 统计5.4.4 映射(map/flatMap)
映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为
map和flatMap:map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

map系列还有mapToInt、mapToLong、mapToDouble三个函数,它们以一个映射函数为入参,将流中每一个元素处理后生成一个新流。
mapToInt三个函数生成的新流,可以进行很多后续操作,比如求最大最小值、求和、求平均值:
5.4.5 归约(reduce)
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
5.4.6 收集(collect)
collect,收集,可以说是内容最繁多、功能最丰富的部分了。把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。collect主要依赖java.util.stream.Collectors类内置的静态方法。
5.4.6.1 归集(toList/toSet/toMap)
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。
toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法。5.4.6.2 统计(count/averaging)
Collectors提供了一系列用于数据统计的静态方法:- 计数:
count
- 平均值:
averagingInt、averagingLong、averagingDouble
- 最值:
maxBy、minBy
- 求和:
summingInt、summingLong、summingDouble
- 统计以上所有:
summarizingInt、summarizingLong、summarizingDouble
5.4.6.3 分组(partitioningBy/groupingBy)
- 分区:将
stream按条件分为两个MapTrue/False
- 分组:将集合分为多个Map,有单级分组和多级分组

5.4.6.4 接合(joining)
joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。5.4.6.5 归约(reducing)
Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持。5.4.7 排序(sorted)
sorted,中间操作。有两种排序:
- sorted():自然排序,流中元素需实现Comparable接口
- sorted(Comparator com):Comparator排序器自定义排序
5.4.8 提取/组合
