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>() {@Overridepublic 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>() {@Overridepublic 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>() {@Overridepublic 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() {@Overridepublic 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{@Overridepublic 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() {@Overridepublic 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{@Overridepublic 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); // 无数个1stream3.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. 通过已有类的APIString 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]// mapstream = 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需要集合一些中间算子,才有价值,否则不如直接使用集合,比如统计集合中有、多少个astream = 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);}
}