Lambda表达式
本质
- Lambda表达式的实质是函数式接口的实例
- 所以以前用匿名内部类表示的,现在都可以用Lambda表达式来写
语法
->:箭头操作符- 左侧:Lambda表达式的形参列表,也就是抽象方法的形参列表
- 右侧:Lambda表达式的方法体,也就是重写的抽象方法的方法体
语法总结
- 左侧:
- Lambda形参列表的参数类型可以省略(类型推断)
- 如果Lambda形参列表只有一个参数,则小括号
()可省略
- 右侧:
- Lambda体应该用大括号
{}包裹 - 如果Lambda体只有一条执行语句(包括
return语句),则大括号{}和return关键字都可以省略
- Lambda体应该用大括号
函数式接口
定义
- 如果一个接口中,只声明了一个抽象方法,则该接口为函数式接口
- 可以在接口上使用
@FunctionalInterface注解,以检查它是否是一个函数式接口
使用举例
Java内置四大核心函数式接口
| 函数式接口 | 参数类型 | 返回类型 | 用途 |
|---|---|---|---|
Consumer<T>消费型接口 |
T |
void |
对类型为T的对象应用操作,包含方法:void accept(T t) |
Supplier<T>供给型接口 |
/ | T |
返回类型为T的对象,包含方法:T get() |
Function<T, R>函数型接口 |
T |
R |
对类型为T的对象应用操作,并返回类型为R的对象,包含方法:R appply(T t) |
Predicate<T>断定型接口 |
T |
boolean |
确定类型为T的对象是否满足某约束,并返回boolean值,包含方法:boolean test(T t) |
其他接口
| 函数式接口 | 参数类型 | 返回类型 | 用途 |
|---|---|---|---|
BiFunction<T, U, R> |
T, U |
R |
对类型为T, U的对象应用操作,返回R类型结果,包含方法:R apply(T t, U u) |
UnaryOperator<T>(Function子接口) |
T |
T |
对类型为T的对象进行一元运算,并返回类型为T的对象,包含方法:T appply(T t) |
BinaryOperator<T>(BiFunction子接口) |
T, T |
T |
对类型为T的对象进行二元运算,并返回类型为T的对象,包含方法:T appply(T t1, T t2) |
BiConsumer<T, U> |
T, U |
void |
对类型为T, U的对象应用操作,包含方法:void accept(T t, U u) |
BiPredicate<T, U> |
T, U |
boolean |
确定类型为T, U的对象是否满足某约束,并返回boolean值,包含方法:boolean test(T t, U u) |
ToIntFunction<T>ToLongFunction<T>ToDoubleFunction<T> |
T |
intlongdouble |
分别计算int、long、double值的函数 |
IntFunction<T>LongFunction<T>DoubleFunction<T> |
intlongdouble |
R |
参数分别为int、long、double类型的函数 |
总结
| 函数式接口名称 | 方法名称 | 参数 | 返回值 |
|---|---|---|---|
Runnable |
run |
无参数 | 无 |
Function |
apply |
1个参数 | 有 |
Consumer |
accept |
1个参数 | 无 |
BiConsumer |
accept |
2个参数 | 无 |
Supplier |
get |
无参数 | 有 |
方法引用与构造器引用
方法引用 Method References
本质
- 方法引用本质上是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字指向一个方法
- 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
格式
类/对象::方法名- 有三种情况:
对象::实例方法名类::静态方法名类::实例方法名
要求
- 实现函数式接口的抽象方法的参数列表和返回类型,必须与方法引用的方法的参数列表和返回类型一致(针对情况1和2)
构造器引用
本质
- 和方法引用类似
- 函数式接口的抽象方法的参数列表和构造器的参数列表一致,抽象方法的返回类型即为构造器所属的类的类型
格式
类::new,如String::new
数组引用
本质
- 可以将数组看做是一个特殊的类,则写法与构造器引用一致
格式
数组名::new,如String[]::new
Stream API
本质
- Stream关注的是对数据的运算,与CPU打交道
- 集合关注的是数据的存储,与内存打交道
特性
- Stream自己不会存储数据
- Stream不会改变源对象;相反,它们会返回一个持有结果的新Stream
- Stream操作是延迟执行的,这意味着它们会等到需要结果的时候才执行
执行流程
1. Stream的实例化,创建Stream
方式一:通过集合
- Java8中的
Collection接口被扩展,可通过两个默认方法获取流default Stream<E> stream():返回一个顺序流default Stream<E> parallelStream():返回一个并行流
1 |
|
方式二:通过数组
- Java8中的
Arrays的静态方法stream()可以获取流static <T> Stream<T> stream(T[] array)
- 重载形式,能够处理对应基本类型的数组:
public static IntStream stream(int[] array)public static LongStream stream(long[] array)public static DoubleStream stream(double[] array)
1 |
|
方式三:通过Stream的of方法
- 可以调用Stream类静态方法
of(),通过显示值创建一个流,它可以接收任意数量的参数public static <T> Stream<T> of(T... values)
1 |
|
方式四:创建无限流
- 可以使用静态方法
Stream.iterate()和Stream.generate()创建无限流- 迭代:
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) - 生成:
public static<T> Stream<T> generate(Supplier<T> s)
- 迭代:
1 |
|
2. 一系列中间操作(filter,map,…)
筛选与切片
| 方法 | 描述 |
|---|---|
filter(Predicate p) |
接收Lambda,从流中排除某些元素 |
distinct() |
筛选,通过流所生成元素的hashCode()和equals()去除重复元素 |
limit(long maxSize) |
截断流,使其元素不超过给定数量 |
skip(long n) |
跳过元素,返回一个去掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补 |
1 |
|
映射
| 方法 | 描述 |
|---|---|
map(Function f) |
接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 |
mapToDouble(ToDoubleFunction f) |
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream |
mapToInt(ToIntFunction f) |
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream |
mapToLong(ToLongFunction f) |
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream |
flatMap(Function f) |
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
1 | // StreamAPITest.java |
map和flatMap,可以类比成List的add和addAll
排序
| 方法 | 描述 |
|---|---|
sorted() |
产生一个新流,其中按自然顺序排序 |
sorted(Comparator com) |
产生一个新流,其中按比较器顺序排序 |
1 |
|
3. 终止操作
匹配与查找
| 方法 | 描述 |
|---|---|
allMatch(Predicate p) |
检查是否匹配所有元素 |
anyMatch(Predicate p) |
检查是否至少匹配一个元素 |
noneMatch(Predicate p) |
检查是否没有匹配所有元素 |
findFirst() |
返回第一个元素 |
findAny() |
返回当前流中的任意元素 |
count() |
返回流中元素总数 |
max(Comparator com) |
返回流中最大值 |
min(Comparator com) |
返回流中最小值 |
forEach(Consumer c) |
内部迭代(使用Collection接口需要用户去做迭代,称为外部迭代;相反,Stream API使用内部迭代,即其内部实现了迭代) |
1 |
|
归约
| 方法 | 描述 |
|---|---|
reduce(T iden, BinaryOperator b) |
可以将流中元素反复结合起来,得到一个值,返回T |
reduce(BinaryOperator b) |
可以将流中元素反复结合起来,得到一个值,返回Optional<T> |
1 |
|
map和reduce的连接通常称为map-reduce模式,常用于分布式和大数据应用
收集
| 方法 | 描述 |
|---|---|
collect(Collector c) |
将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法 |
Collector接口方法中的实现决定了如何对流执行收集的操作,如收集到List,Set,Map等Collectors实用类提供了许多静态方法,可以方便创建常见的收集器实例,具体方法与实例如下表:
| 方法 | 返回类型 | 作用 |
|---|---|---|
toList |
List<T> |
把流中元素收集到List |
toSet |
Set<T> |
把流中元素收集到Set |
toCollection |
Collection<T> |
把流中元素收集到创建的Collection |
counting |
Long |
计算流中元素的个数 |
summingInt |
Integer |
对流中元素的整数属性求和 |
averageingInt |
Double |
计算流中元素Integer属性的平均值 |
summarizingInt |
IntSummaryStatistics |
收集流中Integer属性的统计值,如:平均值 |


说明
- 一个中间操作链,对数据源的数据进行操作
- 一旦执行终止操作,才执行中间操作,并产生结果,也称为”惰性求值“。之后,该Stream不会再被使用
Optional
作用
- 解决空指针异常,如果值存在则
isPresnet()方法会返回true,调用get()方法会返回该对象 Optional<T>类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在;或者仅仅保存null,表示这个值不存在- 原本用
null表示一个值不存在,现在Optional可以更好地表达这一概念,且可以避免空指针异常
创建Optional类对象的方法
| 方法 | 作用 |
|---|---|
Optional.of(T t) |
创建一个Optional实例,t必须非空 |
Optional.empty() |
创建一个空的Optional实例 |
Optional.ofNullable(T t) |
创建一个Optional实例,t可以为null |
判断Optional容器中是否包含对象
| 方法 | 作用 |
|---|---|
boolean isPresent() |
判断是否包含对象 |
void ifPresent(Consumer<? super T> consumer) |
如果有值,就执行Consumer接口的实现代码,并且该值作为参数传给它 |
获取Optional容器的对象
| 方法 | 作用 |
|---|---|
T get() |
如果有值,返回该值,否则抛异常 |
T orElse(T other) |
如果有值则将其返回,否则返回指定的other对象 |
T orElseGet(Supplier<? extends T> other) |
如果有值则将其返回,否则返回由Supplier接口实现提供的对象 |
T orElseThrow(Supplier<? extends X> exceptionSupplier) |
如果有值则将其返回,否则抛出由Supplier接口实现提供的异常 |