1. Lambda 表达式
1.1 概述
Lambda表达式是一种没有名字的函数,也可称为闭包,是java8发布的最重要的新特性。
本质上是一段匿名内部类, 也可以是一段可以传递的代码,lambda表达式也被叫做箭头函数。
闭包: 闭包就是能够读取其它函数内部变量的函数,比如在java中,方法内部的局部变量只能在方法内部使用,所以闭包可以理解为定义在一个函数内部的函数。闭包的本质就是函数内部和函数外部链接起来的桥梁。
1.2 特点
允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用lambda表达式可以使代码变得更加简介紧凑。
和匿名内部类对比:
//和匿名内部类对比
public class Lambda_00 {
public static void main(String[] args) {
// 将数组中的元素逆序输出
Integer[] arr = {1,2,3,5,3,7,9,3,2};
// asList:把数组转换为list集合
List<Integer> integers = Arrays.asList(arr);
// 匿名内部类写法
Collections.sort(integers, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
// lambda表达式写法
Collections.sort(integers,(o1,o2) -> o2 - o1);
System.out.println(integers);
}
}
1.3 应用场景
1.列表迭代 2.Map映射 3.Reduce聚合 4.代替一个不想命名的函数或类,该函数或类一般不会太复杂 5.精简代码
1.4 代码实现
1.4.1 具体语法
1.(parameters) -> expression
2.(parameters) -> (statements)
1.4.2 语法特点
1. 可选类型声明: 不需要声明参数类型,编译器可以统一识别参数值
2. 可选参数圆括号: 只有一个参数时可以不加(),但是有多个参数或者没有参数时,一定要加()
3. 可选的大括号: 如果主体只有一个语句的时候,可以不加大括号,此时分号也不加。如果主体有多行代码的时候,遵守正常的编码规范,该写大括号写大括号,该写分号写分号。
4. 可选的返回关键字: 如果主体只有一个表达式返回值,编译器会自动返回,不需要写return。如果主题有多条语句,需要在大括号里指明表达式要返回的数值,需要写return。
5。 并不是所有的匿名内部类都可以改写成lambda表达式的形式,lambda表达式对应的接口有且只有一个抽象方法。
1.4.3 集合遍历
// 集合遍历
public class Lambda_01 {
public static void main(String[] args) {
String[] arr = {"a", "b", "c"};
// 把数组转换为集合
List<String> list = Arrays.asList(arr);
// 1.8 以前的遍历
for (String string : list) {
System.out.println(string);
}
// 1.8 以后
// 匿名内部类
list.forEach(new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
});
// lambda表达式
list.forEach(i -> System.out.println(i));
}
}
1.4.4 集合排序
// 集合排序
public class Lambda_02 {
public static void main(String[] args) {
Integer[] arr = {1,3,5,2,0,9,4,6};
List<Integer> list = Arrays.asList(arr);
// 1.8以前
Collections.sort(list,new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
// 1.8 以后
list.sort((o1,o2) -> o2 - o1);
System.out.println(list);
}
}
2. 函数式接口
2.1 概述
其本质是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口
核心目的是为了给lambda表达式更好的支持,进一步达到函数式编程的目的
函数式编程可以极大的提高编码的效率
其可以被隐式转换为lambda表达式
2.2 特点
函数式接口有且仅有一个抽象方法,可以包含其它非抽象方法
@FunctionalInterface注解可以检查是否是抽象接口,添加注解后,
接口一定要满足定义,否则报错。只要满足定义就一定是函数式接口,也可以不加注解。
回调函数: 方法的参数是另一个方法,在当前方法中对传递过来的方法进行调用
2.3 应用场景
适用于任何需要提高编码效率的场景,需结合具体情况调用
2.4 代码实现
2.4.1 无参情况
// 无参情况
public class Functional_00 {
public static void main(String[] args) {
// 1. 实现类方式,直接创建子类对象
test(new Test());
// 2. 匿名内部类方式
test(new MyFunctionface() {
@Override
public void print() {
System.out.println("匿名内部类");
}
});
// 3. lambda表达式方式
test(() -> System.out.println("lambda"));
}
public static void test(MyFunctionface myFunctionface) {
myFunctionface.print();
}
}
// 函数式接口
@FunctionalInterface
interface MyFunctionface{
// 抽象方法
void print();
}
class Test implements MyFunctionface{
@Override
public void print() {
System.out.println("子类覆写");
}
}
2.4.2 有参情况
// 有参情况
public class Functional_01 {
public static void main(String[] args) {
// 1. 类实现方式
test(new Test1(), "子类实现");
// 2. 匿名内部类实现
test(new MyFunInterface_1() {
@Override
public void print(String msg) {
System.out.println(msg);
}
}, "匿名内部类");
// lambda表达式实现
test((x) -> System.out.println(x), "lambda表达式");
// 创建函数对象方式
MyFunInterface_1 obj = (x) -> System.out.println(x);
obj.print("函数对象");
}
public static void test(MyFunInterface_1 myFunInterface_1,String msg) {
myFunInterface_1.print(msg);
}
}
@FunctionalInterface
interface MyFunInterface_1{
void print(String msg);
}
class Test1 implements MyFunInterface_1{
@Override
public void print(String msg) {
System.out.println(msg);
}
}
2.5 JDK自带的常用的函数接口
1. Supplier<T>接口
/*
* Supplire<T>接口,代表结果供应商,有返回值,提供一个get方法用于获取数据
*
*/
public class Functional_02 {
public static void main(String[] args) {
test(() -> "你好");
}
public static void test(Supplier<String> supplier) {
System.out.println(supplier.get());
}
}
2. Consumer<T>接口
//Consumer<T>表示消费者,提供了一个accept(T) 方法,有参无返回值
public class Functional_03 {
public static void main(String[] args) {
String msg = "消费";
test(result -> System.out.println(result), msg);
}
public static void test(Consumer<String> fun, String msg) {
fun.accept(msg);
}
}
3. Function<T,R>接口
// Function<T,R> 接口 , R apply(T)有参有返回值,泛型中T表示参数列表类型,R表示返回值类型
public class Functional_04 {
public static void main(String[] args) {
String str = "1234";
test(x -> {int num = Integer.parseInt(str); return num;}, str);
}
public static void test(Function<String,Integer> fun, String msg) {
int num = fun.apply(msg);
System.out.println(num);
}
}
4. Predicate<T>接口
// Predicate<T> 接口,断言,判断相关, boolean test(T)方法
public class Functional_05 {
public static void call(Predicate<String> predicate,String msg) {
System.out.println(predicate.test(msg));
}
public static void main(String[] args) {
String msg = "张三";
call(x -> x.equals("张三"), msg);
}
}
3. 方法引用和构造器调用
3.1 概述
lambda表达式的另一种表现形式,提高方法复用率和灵活性
3.2 特点
更简便,代码量更少,复用性、扩张性更高
3.3 应用场景
若lambda体中的功能,已经有了方法提供实现,可以使用方法引用
不需要在覆写已有API的lambda实现
3.4 代码实现
1. 对象引用::成员方法名
/*
* 成员方法
* 对象引用 :: 成员方法名
* 根据调用方法的入参和出参选择相应函数式接口接收
*
*/
public class FunCall_00 {
public static void main(String[] args) {
Integer i1 = new Integer("123");
System.out.println(i1.equals(123));
// lambda表达式
Predicate<Integer> p = i1::equals;
System.out.println(p.test(123));
}
}
2. 类名::静态方法名
/*
* 静态方法
* 类名::静态方法名
*/
public class FunCall_01 {
public static void main(String[] args) {
System.out.println(Integer.max(4, 3));
BiFunction<Integer, Integer, Integer> bf = Integer::max;
System.out.println(bf.apply(4, 3));
}
}
3. 类名::成员方法名
// 类名::成员方法名
public class FunCall_02 {
public static void main(String[] args) {
BiPredicate<String, String> bp = String :: equals;
System.out.println(bp.test("abc", "abc"));
}
}
4. 构造方法
// 构造方法
public class FunCall_03 {
public static void main(String[] args) {
Object o1 = new Object();
// lambda
// 无参
Supplier<Object> supplier = () -> new Object();
System.out.println(supplier.get());
supplier = Object::new;
System.out.println(supplier.get());
// lambda
// 有参
Function<String, Integer> function = (x) -> new Integer(x);
System.out.println(function.apply("1234"));
function = Integer::new;
System.out.println(function.apply("12"));
}
}
4. Stream API
4.1 概述
数据渠道、管道,用于操作数据源(集合,数组等)所生成的元素序列。
即一组用来处理数组,集合的API
4.2 特点
Stream 不是数据结构,没有内部存储,自己不会存储数据
Stream 不会改变源对象,他们会返回一个持有结果的新Stream
Stream 操作是延迟执行的,这意味着他们会等到需要结果时才执行
Stream 不支持索引访问,支持并行,支持过滤,查找,转换,汇总,聚合等操作
4.3 应用场景
流式计算处理,需要延迟计算,更方便的并行计算,更灵活的集合处理方式的场景。
4.4 运行机制说明
Stream分为源source,中间操作,终止操作。
流的源可以是一个数组,集合,生成器方法,I/O通道等等。
一个流可以有零个或多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用,一个流只会有一个终止操作。
中间操作也称为转换算子-transformation
Stream只有遇到终止操作,它的数据源会开始执行遍历操作。终止操作也称为动作算子-action,因为动作算子的返回值不再是 stream,所以这个计算就终止了
只有碰到动作算子的时候,才会真正的计算
4.5 代码实现
1. 生成Stream流的五种方式
// 生成Stream流的五种方式
public class Stream_00 {
public static void main(String[] args) {
// 1. 数组
String[] str = {"a","c","d","r","f"};
Stream<String> stream1 = Stream.of(str);
// 2. 集合
List<String> list = Arrays.asList(str);
Stream<String> stream2 = list.stream();
// 3. 通过Stream.generate创建
// 创建的是一个无限流(无限大),通常结合limit一起使用,limit限制输出的最大条数
Stream<Integer> stream3 = Stream.generate(() -> 1); // 无数个1
stream3.limit(10).forEach(x -> System.out.println(x)); // 输出10个1
// 4. 通过Stream.iterate创建
// 创建的是一个无限流(无限大),通常结合limit一起使用,limit限制输出的最大条数
// 1 表示起始值为1,返回x+2表示步长为2,里面的数据就是1,3,5,7....
Stream<Integer> stream4 = Stream.iterate(1, x -> x+2);
stream4.limit(10).forEach(x -> System.out.println(x));
// 5. 通过已有类的API
String strs = "sadfwee";
IntStream chars = strs.chars();
chars.forEach(X -> System.out.println(X));
}
}
2. 常用转换算子
filter: 对元素进行过滤筛选,去掉不符合条件的元素
distinct: 去重
skip: 跳过多少条数据
limit: 取一个集合的前几条
map: 对符合条件的数据进行修改,filter返回值true就要,false就不要,但是map不是,map是把返回值重新保存
sorted: 排序
// 常用转换算子
public class Stream_01 {
public static void main(String[] args) {
List<String> strings = Arrays.asList("a","c","d","a","s","f");
// filter : 对元素进行过滤
// collect 时动作算子,把结果返回集合
Stream<String> stream = strings.stream();
List<String> result = stream.filter(x -> x.equals("a")).collect(Collectors.toList());
System.out.println(result); //[a, a]
// 操作完成后Stream需要重新生成
// skip: 跳过多少条数据
stream = strings.stream();
result = stream.skip(2).collect(Collectors.toList());
System.out.println(result);//[d, a, s, f]
// distinct: 去重
stream = strings.stream();
result = stream.distinct().collect(Collectors.toList());
System.out.println(result);//[a, c, d, s, f]
// map
stream = strings.stream();
result = stream.map(x -> x.equals("a")? x + "+++" : x).collect(Collectors.toList());
System.out.println(result);//[a+++, c, d, a+++, s, f]
// sorted: 排序
List<Integer> list = Arrays.asList(1,2,4,2,0,9,1,2);
Stream<Integer> stream1 = list.stream();
// 默认升序
// List<Integer> value = stream1.sorted().collect(Collectors.toList());
// 降序
List<Integer> value = stream1.sorted((o1,o2) -> o2-o1).collect(Collectors.toList());
System.out.println(value); //[9, 4, 2, 2, 2, 1, 1, 0]
}
}
3. 常用动作算子
循环 forEach
计算 min、max、count、average
匹配 anyMatch、allMatch、noneMatch、findFirst、findAny
汇聚 reduce
收集器 collect
// 常用动作算子
public class Stream_02 {
public static void main(String[] args) {
List<String> strings = Arrays.asList("a","c","f","e","c","a");
Stream<String> stream = strings.stream();
// forEach: 遍历
stream.forEach(x -> System.out.println(x));
// count : 统计计数,返回long类型
stream = strings.stream();
long count = stream.count();
System.out.println(count);
// 一般count需要集合一些中间算子,才有价值,否则不如直接使用集合,比如统计集合中有、多少个a
stream = strings.stream();
count = stream.filter(x -> x.equals("a")).count();
System.out.println(count);
// collect 把运算结果封装到集合中
stream = strings.stream();
List<String> result = stream.filter(x -> x.equals("a")).collect(Collectors.toList());
System.out.println(result);
List<Integer> list = Arrays.asList(1,4,5,7,2,99,3);
Stream<Integer> stream1 = list.stream();
// max获取集合中最大元素
int i1 = stream1.max((o1,o2) -> o1-o2).get();
System.out.println(i1);
// 最小值
stream1 = list.stream();
i1 = stream1.min((o1,o2) -> o1-o2).get();
System.out.println(i1);
// anyMatch 是否包含
stream1 = list.stream();
boolean flag = stream1.anyMatch(x -> x==5);
System.out.println(flag);
}
}