Lambda&Stream 叁歲伎倆 2024-04-07 10:35 150阅读 0赞 ## Java 8 Lambda ## ### **1 出现的背景** ### Java 是面向对象语言,除了部分简单数据类型,Java 中的一切都是对象,即使数组也是一种对象,每个类创建的实例也是对象。在 Java 中定义的函数或方法不可能完全独立,也不能将方法作为参数或返回一个方法给实例,在 Java 8 以前,如果我们想要把某些功能传递给某个方法,就需要去写匿名类, 简而言之,在 Java 里将普通的方法或函数像参数一样传值并不简单,为此,Java8之后为了再次简化语法,推出了Lambda表达式。 ### **2 Lambda 表达式简介** ### Lambda 表达式就是一个匿名函数,简化匿名内部类语法,以后可以将匿名函数当做参数传递,由java语言进行推断,补全常规代码; * Lambda 表达式其实就是为了快速产生一个接口的实现类对象; * 使用它可以写出更简洁, 更灵活的代码;体现更紧凑的代码风格,使 java 语言的表达式能力得到的提升; * Lambda 表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,让我们可以使用更少的代码来实现相同的功能。 * 语法糖:也称为糖衣语法,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用,简而言之就是做的事不变,只是把语法进行了简化。 常规代码:匿名内部类写法 //为了对集合进行排序,我们为Comparator接口创建了它的匿名内部类对象,重写接口中的方法,来实现排序功能。 list.sort(new Comparator<User>() { @Override public int compare(User o1, User o2) { return o1.getId()-o2.getId(); } } 简化后:lambda表达式 Arrays.sort(cars,(Car c1,Car c2)->{ return c1.getNum()-c2.getNum(); }); ### 3 Lambda 表达式的使用 ### #### 3.1 Lambda 表达式的格式: #### * 通常使用 (argument) -> \{body\}语法书写 * (参数列表) -> \{函数体\} * 左侧:lambda 表达式的参数列表,也就是函数式接口中抽象方法的参数列表。 * 右侧:lambda 表达式中需要执行的功能,即lambda体,也就是我们要重写的抽象方法的方法体。 **注意:** * Lambda 表达式有零个、一个或多个参数,正文可以包含零条,一条或多条语句。 * 可以显式声明参数的类型,也可以由编译器自动从上下文推断参数的类型,例如 (int a,int b)与 (a,b)相同。 * 当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a\*a。 * 如果 Lambda 表达式的正文只有一条语句,则大括号可不用写;如果有一条以上的语句则必须包含在大括号(代码块)中。 #### 3.2 Lambda 表达式的例子: #### ①无参数,无返回值,lambda 体中只有一行代码时,\{\}可以忽略 * 例如:() -> System.out.println("Hello World"); ②无参数,有返回值 * 例如:() -> \{ return 3.1415 \}; ③有参数,无返回值 * 例如:(String s) -> \{ System.out.println(s); \} ④有一个参数,无返回值 * 例如:s -> \{ System.out.println(s); \} ⑤有多个参数,有返回值 * 例如:(int a, int b) -> \{ return a + b; \} ⑥有多个参数,表达式参数类型可以不写,jvm 可以根据上下文进行类型推断 * 例如:(a, b) -> \{ return a - b; \} 案例: @FunctionalInterface //表示是一个功能函数接口,该接口中只能定义一个抽象方法 public interface Oper { /* lambda表达式接口中,只能有一个抽象方法,因为需要自动类型判断,如果有多个,匿名内部类就对应不了 */ //void show();//①无参数,无返回值 //boolean show();//②无参,有返回值 //void show(int a);//③有参,无返回值 int add(int a,int b);//④有参数,有返回值 } public class TestOper { public void test(Oper oper){ //oper.show();//① //oper.show();//② //oper.show(5);//③ oper.add(2, 5);//④ } public static void main(String[] args) { TestOper testOper = new TestOper(); /*①无参数,无返回值 {}可以忽略 testOper.test(()->System.out.println("显示信息")); testOper.test(()->{ System.out.println("显示信息"); });*/ /*②无参,有返回值 testOper.test(()->{ return false; });*/ /*③有参,无返回值 testOper.test((e)->{ System.out.println(e); });*/ //④有参,有返回值 testOper.test((a,b)->{ return a+b; }); } } ### 4.什么是功能接口(Functional interface) ### Lambda 表达式只支持函数式接口,也就是只有一个抽象方法的接口,功能接口是 java 8 中的新增功能,它们只允许一个抽象方法,这些接口也称为单抽象方法接口。Java 8 也引入了一个注释,即@FunctionalInterface,当你注释的接口违反了 Functional Interface 的契约时,它可以用于编译器级错误。 以下是自定义功能接口的示例: @FunctionalInterface //表示是一个功能函数接口,该接口中只能定义一个抽象方法 public interface Oper { //lambda表达式接口中,只能有一个抽象方法,因为需要自动类型判断,如果有多个,匿名内部类就对应不了 void show();//无返回值,无参数 } 正如其定义所述,功能接口只能有一个抽象方法。如果我们尝试在其中添加一个抽象方法,则会抛出编译时错误。 例如: @FunctionalInterface public interface Oper { void show(); int show(int a,int b); } ## ![22952887c541423d9ddb758db9d31939.png][] ## ## Java8 Stream ## #### **什么是 Stream?** #### Stream流是java8中推出的新功能,与IO流是完全不同的概念, 允许我们以声明式的方式处理**数据集合(数组、容器)**,可以把它看作是遍历数据集的高级迭代器,与 stream 与 lambada 表达示结合后编码效率与大大提高,并且可读性更强。 * Stream流提供了对数据集合进行操作的各种方法,我们只需要声明式的告诉它应该做什么。 * java中的集合、数组主要是存储数据,而Stream的主要作用是对集合进行操作。 * 就是一个对集合进行遍历操作的高级迭代器。 #### **什么是流呢?** #### * 简单的定义,就是“从支持数据处理操作的源,生成的元素序列(流)”。 元素列表: * 和集合一样,流也提供了一个接口,访问特定元素类型的一组有序值。 **数据源 :** * 获取数据的源,比如集合。 **数据处理操作 :**流更偏向于数据处理和计算,比如 filter、map、find、sort 等。 * 简单来说,我们通过一个集合的 stream 方法获取一个流,然后对流进行一系列流操作,最后再构建成我们需要的数据集合。 **获取流** 使用 Collection 接口下的 stream() List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); 使用 Arrays 中的 stream() 方法,将数组转成流 Integer[] nums = new Integer[10]; Stream<Integer> stream = Arrays.stream(nums); 使用 Stream 中的静态方法:of() 可变长度参数 Stream<Integer> stream = Stream.of(1,2,3,4,5,6); 使用 BufferedReader.lines() 方法,将每行内容转成流 BufferedReader reader=new BufferedReader(new FileReader("stream.txt")); Stream<String> lineStream = reader.lines(); 流操作 流操作可以分为两类:**中间操作**和**终端操作**。 List<Apple> apples = applestore .stream() 获得流 .filter(a -> a.getColor().equals("red")) 中间操作 .collect(Collectors.toList()); 终端操作 简化来说就是: 数据源 => 中间操作 => 终端操作 => 结果 诸如 filter 或者 sort 等中间操作会返回另一个流,进而进行下一步流操作,而终端操作则是将流关闭,构建新的数据集合对象(也可以不构建)。 **中间操作** filter:过滤流中的某些元素, sorted(): 自然排序,流中元素需实现 Comparable 接口 distinct: 去除重复元素 limit(n): 获取 n 个元素 skip(n): 跳过 n 元素,配合 limit(n)可实现分页 map(): 将其映射成一个新的元素 **终端操作** forEach: 遍历流中的元素 toArray:将流中的元素倒入一个数组 Min:返回流中元素最小值 Max:返回流中元素最大值 count:返回流中元素的总个数 Reduce:所有元素求和 anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足条件则返 回 true,否则返回 false allMatch:接收一个 Predicate 函数,当流中每个元素都符合条件时才返回 true,否则返回 false findFirst:返回流中第一个元素 collect:将流中的元素倒入一个集合,Collection 或 Map 案例1: public static void main(String[] args) { List<Apple> applestore = new ArrayList<>(); applestore.add(new Apple(1, 1001, "red")); applestore.add(new Apple(2, 1002, "black")); applestore.add(new Apple(3, 1003, "blue")); applestore.add(new Apple(4, 1004, "red")); applestore.add(new Apple(3, 1003, "green")); } 我们的需求是在 applestore 集合中找出红色苹果手机,使用 Stream 流快速实现操作 List<Apple> apples = applestore .stream() .filter(apple -> apple.color.equals("red")) .collect(Collectors.toList()); for (Apple a : apples) { System.out.println(a.no);//1 4 } 这里使用的就是 Java8 中的 stream 流,使用的是声明性方式写的: * 说明想要完成什么(筛选,排序,取值),而不说明如何实现一个操作(for 循环)。 * 同时可以将这些操作链接起来,达到一种流水线式的效果。 * 获取流------------------> 中间操作 ----------------------> 终端操作 数据集合------>Stream------>filter----->Stream----->sorted------>Stream ------>tolist/sum/max-------result [22952887c541423d9ddb758db9d31939.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/07/7644f34664c24f4999ad8e1edab38db2.png
还没有评论,来说两句吧...