2021.11.2
Lambda
概述
Lambda表达式是一种没有名字的函数,也可称为闭包,是Java 8 发布的最重要新特性。
本质上是一段匿名内部类,也可以是一段可以传递的代码。
还有叫箭头函数的...
为什么使用Lambda表达式
Lambda表达式就是一个匿名内部类的简写方式
使程序更加简洁清晰,编程效率也得到了提高
package study; public class Part01_Lambda { public static void main(String[] args) { int[] arr = { 1, 2, 3, 4 }; // 常规遍历 // for (int j : arr) { // System.out.println(i); // } // 1 实现类 // forEach(arr ,new A()); // 2 匿名内部类 forEach(arr, new Array() { @Override public void m1(int i) { System.out.println(i + "-====-"); } }); // 3 lambda表达式 forEach(arr, (i) -> System.out.println(i +1+ "++++++++++")); } // 封装的功能,完成遍历操作,但是需要传递数组,和要做的事 // 该方法只是帮我们遍历而已 public static void forEach(int[] arr, Array array) { for (int i : arr) { array.m1(i); } } } // 封装要做的事 interface Array { void m1(int i); } class A implements Array { @Override public void m1(int i) { System.out.println(i + "---"); } }
规则:
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
如果不写{} return 不能写 分号不能写
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值
如果只有一条语句,并且是返回值语句,就可以不写return 不写 {}
如果写上{} 就必须写return 和 ;
如果有 多条语句,必须写{} return 和 ; 也必须写
例子:
package study; import java.util.ArrayList; import java.util.List; public class Part02_Lambda_02 { public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(2); list.add(3); // 常规写法 // for (Integer integer : list) { // System.err.println(integer); // } // 匿名内部类写法 // list.forEach(new Consumer<Integer>() { // @Override // public void accept(Integer t) { // System.out.println(t); // } // }); // lambda写法 list.forEach(x -> System.out.println(x)); } }
package study; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Part03_Lambda_03 { public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); list.add(11); list.add(2); list.add(3); // Collections.sort(list, new Comparator<Integer>() { // @Override // public int compare(Integer o1, Integer o2) { // return o2-o1; // } // }); Collections.sort(list, (x, y) -> y - x); System.out.println(list); } } class B { @Override public boolean equals(Object obj) { return false; } }
函数式接口
介绍
英文称为Functional Interface
其本质是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
核心目标是为了给Lambda表达式的使用提供更好的支持,进一步达到函数式编程的目标,可通过运用函数式编程极大地提高编程效率。
其可以被隐式转换为 lambda 表达式。
特点
函数式接口是仅制定一个抽象方法的接口
可以包含一个或多个静态或默认方法
专用注解即@FunctionalInterface 检查它是否是一个函数式接口,也可不添加该注解
如果有两个或以上 抽象方法,就不能当成函数式接口去使用,也不能添加@FunctionalInterface这个注解
如果只有一个抽象方法,那么@FunctionalInterface注解 加不加 都可以当做函数式接口去使用
无参构造与有参构造:
package study; import java.util.function.Function; import java.util.function.Supplier; public class Part04_Constryctarcakk { public static void main(String[] args) { // 无参构造 Supplier<Object> objSi = Object::new; System.out.println(objSi.get()); // 有参构造 Function<String, Integer> function = Integer::new; System.out.println(function.apply("123")); // new Integer("123"); } }
无参情况 public class FunInterface_01 { // 自定义静态方法,接收接口对象 public static void call(MyFunctionInter func) { // 调用接口内的成员方法 func.printMessage(); } public static void main(String[] args) { // 第一种调用 : 直接调用自定义call方法,传入函数 FunInterface_01.call(() -> { System.out.println("HelloWorld!!!"); }); // 第二种调用 : 先创建函数对象,类似于实现接口的内部类对象 MyFunctionInter inter = () -> { System.out.println("HelloWorld2!!!!"); }; // 调用这个实现的方法 inter.printMessage(); } } // 函数式接口 @FunctionalInterface interface MyFunctionInter { void printMessage(); }
有参情况 public class FunInterface_02 { // 自定义静态方法,接收接口对象 public static void call(MyFunctionInter_02 func, String message) { // 调用接口内的成员方法 func.printMessage(message); } public static void main(String[] args) { // 调用需要传递的数据 String message = "有参函数式接口调用!!!"; // 第一种调用 : 直接调用自定义call方法,传入函数,并传入数据 FunInterface_02.call((str) -> { System.out.println(str); }, message); // 第二种调用 : 先创建函数对象,类似于实现接口的内部类对象 MyFunctionInter_02 inter = (str) -> { System.out.println(str); }; // 调用这个实现的方法 inter.printMessage(message); } } // 函数式接口 @FunctionalInterface interface MyFunctionInter_02 { void printMessage(String message); }
Supplier<T>接口 代表结果供应商,所以有返回值,可以获取数据
有一个get方法,用于获取数据
package study; import java.util.function.Supplier; /** * Supplier 代表供应商,有返回值,可以获取数据使用 * * 提供一个get方法 * @author 落华见樱 * */ public class Part07_Functions_02 { public static void main(String[] args) { String string = "张三"; String result = getResult(()->"我叫"+string); System.out.println(result); } public static String getResult (Supplier<String> supplier){ return supplier.get(); } }
Consumer<T>接口 消费者接口所以不需要返回值
有一个accept(T)方法,用于执行消费操作,可以对给定的参数T 做任意操作
package study; import java.util.function.Consumer; /** * Consumer 代表了消费者,提供了accept方法 有参数 但是无返回值 * @author 落华见樱 * */ public class Part06_Functions_01 { public static void main(String[] args) { int[] arr = { 1, 2, 3, 4 }; // 常规遍历 // for (int j : arr) { // System.out.println(i); // } // 3 lambda表达式 forEach(arr, (i) -> System.out.println(i + "++++++++++")); } // 封装的功能,完成遍历操作,但是需要传递数组,和要做的事 // 该方法只是帮我们遍历而已 public static void forEach(int[] arr, Consumer<Integer> c) { for (int i : arr) { c.accept(i); } } }
Function<T,R>接口 表示接收一个参数并产生结果的函数
顾名思义,是函数操作的
有一个R apply(T)方法,Function中没有具体的操作,具体的操作需要我们去为它指定,因此apply具体返回的结果取决于传入的lambda表达式
package study; import java.util.function.Function; /** * Function : 有参有返回值,提供了 apply方法 * @author 落华见樱 * */ public class Part08_Functions_03 { public static Integer m1(String string ,Function<String, Integer> fun){ return fun.apply(string); } public static void main(String[] args) { String string = "123"; int result = m1(string,s->Integer.parseInt(s)*10); System.out.println(result); } }
Predicate<T>接口 断言接口
就是做一些判断,返回值为boolean
有一个boolean test(T)方法,用于校验传入数据是否符合判断条件,返回boolean类型
public class _06_JdkOwn_04 { // 自定义方法,并且 Predicate 接收String字符串类型 public static void call(Predicate<String> predicate, String isOKMessage) { boolean isOK = predicate.test(isOKMessage); System.out.println("isOK吗:" + isOK); } public static void main(String[] args) { // 传入的参数 String input = "ok"; call((String message) -> { // 不区分大小写比较,是ok就返回true,否则返回false if (message.equalsIgnoreCase("ok")) { return true; } return false; }, input); } }
方法引用和构造器调用
package study; import java.util.function.Supplier; /** * 对象引用::成员方法名 * * 要求 : 需要根据调用方法的入参和出参去选择对应的函数式接口才行 * @author 落华见樱 * */ public class Part09_FunCall_01 { public static void main(String[] args) { Integer i1 = new Integer(123); // 常规lambda写法 Supplier<String> su = () -> i1.toString(); System.out.println(su.get()); // 方法引用写法 su = i1::toString; System.out.println(su.get()); } }
package study; import java.util.function.BiFunction; import java.util.function.Function; /** * 类名::静态 * @author 落华见樱 * */ public class Part10_FunCall_02 { public static void main(String[] args) { Function<String, Integer> fun = Integer::parseInt; System.out.println(fun.apply("123")); fun = new Function<String, Integer>() { @Override public Integer apply(String t) { // TODO Auto-generated method stub return null; } }; fun = x -> Integer.parseInt(x); fun = Integer::parseInt; // 前两个是入参,第三个是返回值 BiFunction<Integer, Integer, Integer> bif = Integer::max; System.out.println(bif.apply(123, 323)); test((B123 x) -> System.out.println(x)); } public static void test(Aasa a) { } } interface Aasa { public void m1(B123 b); } class B123 { }
package study; import java.util.function.BiPredicate; /** * 类名::成员方法名 * @author 落华见樱 * */ public class Part11_FunCall_03 { public static void main(String[] args) { BiPredicate<String, String> bp = String::equals; System.out.println(bp.test("abc", "abc")); } }
package study; import java.util.function.Function; public class Part05_ArrayCall { public static void main(String[] args) { Function<Integer, Integer[]> function = Integer[]::new; Integer[] arr = function.apply(10); for (Integer integer : arr) { System.out.println(integer); } } }
Stream API
概念说明
数据渠道、管道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,流讲的是计算
即一组用来处理数组,集合的API。
特点
Stream 不是数据结构,没有内部存储,自己不会存储元素。
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
不支持索引访问。
延迟计算
支持并行
很容易生成数据或集合
支持过滤,查找,转换,汇总,聚合等操作。
应用场景
流式计算处理,需要延迟计算、更方便的并行计算
更灵活、简洁的集合处理方式场景
代码实现
运行机制说明
Stream分为源source,中间操作,终止操作。
流的源可以是一个数组,集合,生成器方法,I/O通道等等。
一个流可以有零个或多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用,一个流只会有一个终止操作。
中间操作也称为转换算子-transformation
Stream只有遇到终止操作,它的数据源会开始执行遍历操作。
终止操作也称为动作算子-action
因为动作算子的返回值不再是 stream,所以这个计算就终止了
只有碰到动作算子的时候,才会真正的计算
package study; import java.util.Arrays; import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; /** * 生成流 * @author 落华见樱 * */ public class Part12_Stream { public static void main(String[] args) { // 1 数组转换为流 String[] strings = { "q", "w", "e", "r" }; Stream<String> stream1 = Stream.of(strings); // 2 通过集合 // Arrays.asList : 把数组转换为集合 List<String> list = Arrays.asList(strings); stream1 = list.stream(); // 3 通过Stream的generate创建 // 但是是一个无限流(无限大),所以经常结合limit一起使用,来限制最大个数 // generate参数是一个 Supplier ,而 Supplier中有一个get方法用于获取数据 // 而 get方法的返回值,会作为数据保存到这个流中,下面程序也就意味中该流中的数据都是1 Stream<Integer> stream2 = Stream.generate(()->1); // limit 是中间操作,设置流的最大个数 // forEach 遍历,是终止操作 stream2.limit(5).forEach(x->System.out.println(x) ); // 4 通过String.iterate // 同上,是无限流 // 参数1 是起始值, 参数二 是function, 有参有返回值 // x+2 等于步长为二 for(int i =1 ; true ; i+=2) Stream<Integer> stream3 = Stream.iterate(1, x->x+2); stream3.limit(10).forEach(x->System.out.println(x)); // 5 已有类的API String string = "abc"; IntStream chars = string.chars(); chars.forEach(x->System.out.println(x)); } }