当前位置: 首页 > news >正文

Java——lambda表达式和StreamAPI

一、lambda

1. lambda表达式

1.1 Lambda表达式的使用举例:

  • (o1,02)->Integer.compare(o1,o2);

1.2 Lambda表达式的格式举例:

  • Lambda形参列表->lambda

1.3 Lambda表达式的格式

lambda操作符或箭头操作符

  • 的左边:lambda形参列表,对应着要重写的接口中的抽象方法的形参列表。
  • 的右边:lambda体,对应着接口的实现类要重写的方法的方法体。

1.4 Lambda表达式的本质

  • 一方面,lambda表达式作为接口的实现类的对象。--->"万事万物皆对象
  • 另一方面,lambda表达式是一个匿名函数。

lambda使用格式实例

    //lambda表达式的使用练习1//lambda需要两个或以上的参数时,多条执行语句,并有返回值时@Testpublic void test7(){Comparator<Integer> com1 = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {}};int compare1 = com1.compare(21,35);System.out.println(compare1);System.out.println("**********下面为使用lambda表达式*********");Comparator<Integer> com2 = (o1,o2) ->{System.out.println(o1);System.out.println(o2);return o1.compareTo(o2);};int compare2 = com2.compare(12,32);System.out.println(compare1);}//lambda表达式的使用练习2//两个参数,一个返回值@Testpublic void test7(){Comparator<Integer> com1 = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1,o2);}};int compare1 = com1.compare(21,35);System.out.println(compare1);System.out.println("**********下面为使用lambda表达式*********");Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2);int compare2 = com2.compare(12,32);System.out.println(compare1);}//lambda表达式的使用练习3//无参,无返回值@Testpublic void test10(){Runnable r1 = new Runnable() {@Overridepublic void run() {System.out.println("去北京喝豆汁");}};r1.run();System.out.println("**********下面为使用lambda表达式*********");Runnable r2 = () -> System.out.println("去四川吃火锅");r2.run();}//lambda表达式的使用练习4//只有一个参数@Testpublic void test11(){Consumer<String> con = new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}};con.accept("好像在哪里见过?");Consumer<String> con1 = s -> System.out.println(s);con1.accept("我想在梦里遇见你!");}

1.5.函数式接口:
问题一:什么是函数式接口?为什么需要函数式接口?

  • 如果接口中只声明有一个抽象方法,则此接口就称为函数式接口。
  • 因为只有给函数式接口提供实现类的对象时,我们才可以使用lambda表达式:

问题二:api中函数式接口所在的包

  • jdk8中声明的函数式接口都在java.util.function包下

问题三:4个基本的函数式接口

接口名称称谓参数类型返回类型用途
Consumer<T>消费型接口T对类型为 T 的对象应用操作,包含方法: void accept(T t)
Supplier<T>供给型接口T返回类型为 T 的对象,包含方法: T get()
Function<T, R>函数型接口TR对类型为 T 的对象应用操作,并返回结果,包含方法: R apply(T t)
Predicate<T>判断型接口Tboolean确定类型为 T 的对象是否满足某约束,包含方法: boolean test(T t)

函数式接口的使用实例:

/*ConsumerConsumer<T> void accept(T t);*///语法格式:lambda表达式使用时,只有一个参数,可以省略掉()@Testpublic void test11(){Consumer<String> con = new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}};con.accept("好像在哪里见过?");Consumer<String> con1 = s -> System.out.println(s);con1.accept("我想在梦里遇见你!");}/*Supplier<T> T get();*/@Testpublic void test12(){//构造器引用Supplier<Person> sup1 = Person :: new;System.out.println(sup1.get());}/*Function<T, R> R apply(T t)*/@Testpublic void test13(){Function<Integer,Person> fun1 = Person::new;System.out.println(fun1.apply(14));}/*BiFunction<T, U, R>  R apply(T t, U u)*/@Testpublic void test14(){BiFunction<String, Integer, Person> bif = Person::new;System.out.println(bif.apply("Tom", 18));}

1.6 语法规则总结;

  • ->的左边:lambda形参列表,参数的类型都可以省略。如果形参只有一个,则一对()也可以省略
  • ->的右边:lambda体,对应着重写的方法的方法体。如果方法体中只有一行执行语句,则一对{可以省略,若有return关键字,则必须一并省略。

2. 方法引用

2.1 方法的格式:

  • Integer::compare

2.2 方法引用的埋解

  • 方法引用,可以看做是基于lambda表达式的进一步刻画
  • 当需要提供一个函数式接口的实例时,我们可以使用lambda表达式提供此实例。

>当满足一定的条件的情况下,我们还可以使用方法引用或构造器引用替换lambda表达式

2.3 方法引用的本质:

  • 方法引用作为了函数式接口的实例。--->“万事万物皆对象"

2.4 格式:

  • 类(或对象)::方法名

2.5.具体使用情况

情况1: 对象::实例方法
  • 要求:函数式接口中的抽象方法a与其内部实现时调用的对象的某个方法b的形参列表和返回值类都相同
  • 此时,可以考虑使用方法b实现对方法a的替换、覆盖。此替换或覆盖即为方法引用。

注意:此方法b是非静态的方法,需要对象调用

代码实例
/*情况一:对象::实例方法* Consumer中的void accept(T t)* PrintStream中的void println(T t)* */@Testpublic void test1(){Consumer<String> con = new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}};con.accept("喜羊羊与灰太狼");//lambda表达式Consumer<String> con1 = s->System.out.println(s);con1.accept("美羊羊与沸羊羊");//方法引用Consumer<String> con2 = System.out::println;con2.accept("懒洋洋的睡一觉");}/*Supplier中的get()方法* Employee中的String getName()* */@Testpublic void test2(){Employee emp = new Employee(1002,"马云",2, 9876.12);Supplier<String> sup1 = new Supplier<String>() {@Overridepublic String get() {return emp.getName();}};System.out.println(sup1.get());//lambda方法Supplier<String> sup2 = ()-> emp.getName();System.out.println(sup2.get());//方法引用Supplier<String> sup3 = emp::getName;System.out.println(sup3.get());}
情况2: 类::静态方法

要求:

  • 函数式接口中的抽象方法a与其内部实现时调用的类的某个静态方法b的形参列表和返回值类型都相同(或一致),此时,可以考虑使用方法b实现对方法a的替换、覆盖。此替换或覆盖即为方法引用。

注意:此方法b是静态的方法,需要类调用。

代码实例
//情况二:类::静态方法/** Comparator中的int compare(T t,T t)* Integer中的int compare(T t,T t)* */@Testpublic void test3(){Comparator<Integer> com1 = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1,o2);}};System.out.println(com1.compare(12, 21));//lambda表达式Comparator<Integer> com2 = (o1 ,o2)->Integer.compare(o1,o2);System.out.println(com2.compare(21, 22));//方法引用Comparator<Integer> com3 = Integer::compare;System.out.println(com3.compare(44, 43));}/** Function中的apply(T t)* Math中的long round(Double d)* */@Testpublic void test4(){Function<Double,Long> fun1 = new Function<Double, Long>() {@Overridepublic Long apply(Double aDouble) {return Math.round(aDouble);//四舍五入}};System.out.println(fun1.apply(1.4));Function<Double,Long> fun2 = aDouble -> Math.round(aDouble);System.out.println(fun2.apply(1.6));Function<Double,Long> fun3 = Math::round;System.out.println(fun3.apply(2.1));}
情况3: 类::实例方法

要求:

  • 函数式接口中的抽象方法a与其内部实现时调用的对象的某个方法b的返回值类型相同。同时,抽象方法a中有n个参数,方法b中有n-1个参数,且抽象方法a的第1个参数作为方法b的调用者,且抽象方法a的后n-1个参数与方法b的n-1个参数的类型相同(或一致)。则可以考虑使用方法b实现对方法a的替换、覆盖。此替换或覆盖即为方法引用

注意:此方法b是非静态的方法,需要对象调用。但是形式上,写出对象a所属的类

代码实例
    //情况三:类::实例方法(难)/** Comparator的int compare(T t,T t)* String中的int t1.compare(t2)*  */@Testpublic void test5(){Comparator<String> com1 = new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}};//方法引用Comparator<String> com2 = String::compareTo;System.out.println(com2.compare("abb","abb"));}/** BiPredicate中的boolean test(T t1,T t2)* String中的boolean t1.equals(t2)* */@Testpublic void test6(){BiPredicate<String,String> bip = new BiPredicate<String, String>() {@Overridepublic boolean test(String s1, String s2) {return s1.equals(s2);}};System.out.println(bip.test("10001", "10001"));//lambda表达式BiPredicate<String,String> bip1 = (s1,s2)->s1.equals(s2);System.out.println(bip.test("103", "1031"));//方法引用BiPredicate<String,String> bip2 = String::equals;System.out.println(bip.test("101", "10"));}/** Function中的R apply(T t)* Employee中的string getName();* */@Testpublic void test7(){Employee emp = new Employee(1003,"刘强东",33,3000.82);Function<Employee,String> fun1 = new Function<Employee, String>() {@Overridepublic String apply(Employee employee) {return emp.getName();}};System.out.println(fun1.apply(emp));//方法引用Function<Employee,String> fun2 = Employee::getName;System.out.println(fun2.apply(emp));}
}

3. 构造器引用

3.1 格式

  • 类名:: new

3.2 说明

  • 调用了类名对应的类中的某一个确定的构造器
  • 具体调用的是类中的哪一个构造器呢?取决于函数式接口的抽象方法的形参列表!

3.3 代码示例

    /*Supplier<T> T get();*/@Testpublic void test12(){//构造器引用Supplier<Person> sup1 = Person :: new;System.out.println(sup1.get());}/*Function<T, R> R apply(T t)*/@Testpublic void test13(){Function<Integer,Person> fun1 = Person::new;System.out.println(fun1.apply(14));}/*BiFunction<T, U, R>  R apply(T t, U u)*/@Testpublic void test14(){BiFunction<String, Integer, Person> bif = Person::new;System.out.println(bif.apply("Tom", 18));//调用的是参数Integer和String类型,所以出来的是名字和年龄}

4. 数组引用

格式: 数组名[]::new

代码示例

//  数组引用@Testpublic void test15(){Function<Integer, Person[]> fun2 = new Function<Integer, Person[]>() {@Overridepublic Person[] apply(Integer length) {return new Person[length];}};System.out.println(fun2.apply(13).length);Function<Integer,Person[]> fun1 = Person[] :: new;System.out.println(fun1.apply(14).length);//数组的长度等于14}

二、StreamAPI

前言:

问题 :什么是Stream?
Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
Stream 和 Collection 集合的区别: Collection 是一种静态的内存数据结构,讲的是数据,而 stream 是有关计算,讲的是计算。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过 CPU 实现计算。

1.Stream API vs 集合框架

  • Stream API 关注的是多个数据的计算(排序、查找、过滤、映射、遍历等),面向CPU的。
  • 集合关注的数据的存储,面向内存的。

即 :Stream API 之于集合,类似于SOL之于数据表的查询。

2.使用说明

  • Stream 自己不会存储元素。
  • Stream不会改变源对象。相反,他们会返回一个持有结果的新stream。
  • Stream, 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果。
  • Stream一旦执行了终止操作,就不能再调用其它中间操作或终止操作了。

3.Stream 执行流程

Stream的操作三个步骤
1.创建 stream

  • 一个数据源(如:集合、数组),获取一个流

2.中间操作

  • 每次处理都会返回一个持有结果的新stream,即中间操作的方法返回值仍然是stream类型的对象。因此中间操作可以是个 操作链 ,可对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行。

3.终止操作

  • 终止操作的方法返回值类型就不再是stream了,因此一旦执行终止操作,就结束整个stream操作了。一旦执行终止操作,就执行中间操作链,最终产生结果并结束stream。

图示:

3.1 Stream的实例化

1.三种实例化的方式
  • 方式一:通过集合创建Stream实例
  • 方式二:通过数组创建Stream实例
  • 方式三:通过Stream.of()方式创建Stream实例
2.实例化代码实现
package data;import org.junit.Test;import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;public class StreamAPiTest {//创建Stream方式一:通过集合@Testpublic void test1(){List<Employee> list = EmployeeData.getEmployees();//返回一个顺序流Stream<Employee> stream = list.stream();//返回一个并行流Stream<Employee> stream1 = list.parallelStream();System.out.println(stream);System.out.println(stream1);}//创建Stream方式二:使用数组@Testpublic void test2(){//调用Arrays类的static <T>  Stream <T> Stream(T[] array): 返回一个流Integer[] arr = new Integer[] {1,2,3,4,5};Stream<Integer> stream = Arrays.stream(arr);int[] arr1 = new int[]{1,2,3,4,5};IntStream stream1 = Arrays.stream(arr1);}//创建Stream方式三:通过Stream的of()@Testpublic void test3(){Stream<String> stream = Stream.of("AA","BB","CC","DD","EE");}
}

3.2 中间操作

1.筛选与切片
方法描述
filter(Predicate p)接收一个 Lambda ,过滤掉不符合条件的元素。
distinct()筛选,通过流所生成元素的 hashCode() 和 equals() 方法去除重复元素。
limit(long maxSize)截断流,使其元素数量不超过指定数量。
skip(long n)跳过前 n 个元素,返回一个新的流。若流中元素不足 n 个,则返回一个空流,与 limit(n) 互补。

2.映射
方法描述
map(Function f)接收一个函数作为参数,该函数应用于每个元素,并映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数应用于每个元素,产生一个新的 DoubleStream
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数应用于每个元素,产生一个新的 IntStream
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数应用于每个元素,产生一个新的 LongStream
flatMap(Function f)接收一个函数作为参数,将流中的每个值换成另一个流,然后将所有流连接成一个流。
3.排序 
方法描述
sorted()产生一个新流,其中按自然顺序排序。
sorted(Comparator com)产生一个新流,其中按提供的比较器顺序排序。
4.中间操作代码实现
//中间操作//筛选与切片@Testpublic void test4(){//filter(Predicate p),接收lambda方法---移除某些元素//集合实例化List<Employee> list = EmployeeData.getEmployees();//通过集合创建Stream的实例Stream<Employee> stream = list.stream();//查询员工表中薪资大于7000的员工的信息stream.filter(emp -> emp.getSalary() > 7000).forEach(System.out::println);System.out.println("************************");//由于在上面Stream已经实行了终止操作,所以不可以在调用其他方法的终止操作//limit--截断流,使其元素不超过指定数量list.stream().filter(emp->emp.getSalary() > 7000).limit(2).forEach(System.out::println);//skip--跳过流,跳过前n个元素list.stream().skip(4).forEach(System.out::println);}//映射@Testpublic void test5(){//map(Function f)接收一个函数作为参数,该函数应用于每个元素,并映射成一个新的元素。//转换为大写   toUpperCase() 将字符串转换为大写List<String> list = Arrays.asList("aa","bb","cc","dd");list.stream().map(String::toUpperCase).forEach(System.out::println);//获取姓名长度大于3的员工的姓名List<Employee> employees = EmployeeData.getEmployees();//先过滤后映射employees.stream().filter(emp->emp.getName().length() > 3).map(Employee::getName).forEach(System.out::println);//先映射后过滤employees.stream().map(Employee::getName).filter(name->name.length()>3).forEach(System.out::println);}//排序@Testpublic void test6(){//sorted()--自然排序Integer[] arr = new Integer[]{12,34,65,23,1,23,4,45};String[] arr1 = new String[]{"SS","EE","CC","BB","AA"};//使用数组创建Stream实例Arrays.stream(arr).sorted().forEach(System.out::println);//原本的数组并没有升序,而调整//Stream不会改变原对象System.out.println(Arrays.toString(arr));//sorted(comparator c)--定制排序Arrays.stream(arr1).sorted(String::compareTo).forEach(System.out::println);}

3.3 终止操作

1.匹配与查找
方法描述
allMatch(Predicate p)检查是否匹配所有元素
anyMatch(Predicate p)检查是否至少匹配一个元素
noneMatch(Predicate p)检查是否没有匹配任何元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素
count()返回流中元素的总数
max(Comparator c)返回流中元素的最大值
min(Comparator c)返回流中元素的最小值
forEach(Consumer c)对流中的每个元素执行给定的操作(内部迭代)
2.归约
方法描述
reduce(T identity, BinaryOperator b)可将流中元素反复结合起来,得到一个值。返回T
reduce(BinaryOperator b)可将流中元素反复结合起来,得到一个值。返回 Optional<T>
3.收集
方法描述
collect(Collector c)将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中的元素汇总的方法
4.终止操作代码实现
//终止操作//匹配与查找@Testpublic void test7(){//allMatch检查是否匹配所有元素//是否所有员工都大于20岁List<Employee> list = EmployeeData.getEmployees();System.out.println(list.stream().allMatch(emp -> emp.getAge() > 20));//anyMatch检查是否至少一个元素满足要求System.out.println(list.stream().anyMatch(emp -> emp.getSalary() > 9600));//findFirst返回第一个元素System.out.println(list.stream().findFirst().get());//findAny返回任意一个元素System.out.println(list.stream().findAny().get());//count 返回流中元素的总数System.out.println(list.stream().filter(emp -> emp.getSalary() > 7000).count());//max(Comparator c) 返回流中最大值System.out.println(list.stream().max((a1, a2) -> Integer.compare(a1.getAge() , a2.getAge())));//min(Comparator c) 返回流中最大值System.out.println(list.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));//forEach(Consumer c),遍历list.stream().forEach(System.out::println);}//归约@Testpublic void test8(){List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);//reduce(T identity, BinaryOperator b)//可将流中元素反复结合起来,得到一个值。返回T//操作;计算1-10的和System.out.println(list.stream().reduce(0, (s1, s2) -> s1 + s2));//前面的0或者10,相当于起始值System.out.println(list.stream().reduce(5, (s1, s2) -> s1 + s2));System.out.println(list.stream().reduce(15, Integer::sum));//reduce(BinaryOperator b)//可将流中元素反复结合起来,得到一个值。返回Optional<T>//操作:计算公司所有员工工资的总额List<Employee> list1 = EmployeeData.getEmployees();//先映射后计算list1.stream().map(emp->emp.getSalary()).reduce((s1,s2)->Double.sum(s1,s2));list1.stream().map(emp->emp.getSalary()).reduce(Double::sum);}//收集@Testpublic void test9(){/*collect(Collector c)将流转换为其他形式。接收一个Collector接口的实现用于给Stream中的元素汇总的方法*///题目1:查找工资大于6000的员工,结果返回set或listList<Employee> list = EmployeeData.getEmployees();List<Employee> list1 = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toList());Set<Employee> list2 = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toSet());list.forEach(System.out::println);System.out.println();list1.forEach(System.out::println);System.out.println();list2.forEach(System.out::println);System.out.println();//题目2:按照员工的年龄排序,返回一个list//升List<Employee> list3 = list.stream().sorted((e1,e2)->e1.getAge()-e2.getAge()).collect(Collectors.toList());list3.forEach(System.out::println);System.out.println();//降List<Employee> list4 = list.stream().sorted((e1,e2)-> -(e1.getAge()-e2.getAge())).collect(Collectors.toList());list4.forEach(System.out::println);}

http://www.mrgr.cn/news/61538.html

相关文章:

  • 【python小工具】怎么获取视频的关键帧频率?
  • 基于DFT与IIR-FIR滤波器的音频分析与噪声处理
  • 算法初学者(图的存储)链式前向星
  • 三维卷积( 3D CNN)
  • Spring bean的生命周期和扩展
  • C语言gdb调试
  • AI-Talk开发板之启动问题
  • Windows 下实验视频降噪算法 MeshFlow 详细教程
  • Java学习第六天~第七天-程序控制结构:
  • 《操作系统真象还原》第4章 保护模式入门
  • 使用Node.js内置的http模块创建简单的HTTP服务器,并根据请求的路径返回不同的文本响应。
  • LeetCode 3211.生成不含相邻零的二进制字符串:二进制枚举+位运算优化
  • 计算机毕业设计——ssm基于HTML5的互动游戏新闻网站的设计与实现录像演示2021
  • modelsim命令:add atv
  • 【Java数据结构】树】
  • 基于SSM积分商城管理系统的设计与实现(源码+lw+部署文档+讲解等)
  • 小红书图文无水印下载
  • 进一步认识ICMP协议
  • MySQL用户权限管理属于SQL语句中的DCL语句
  • 深入理解阻塞队列
  • 鸿蒙生态崛起:开发者的机遇与挑战
  • 数据结构————map,set详解
  • Rust实现Kafka - 前言
  • 18 Docker容器集群网络架构:一、etcd 概述
  • windows 驱动实例分析系列: NDIS 6.0的Filter 驱动改造(一)
  • Ubuntu下搭建自己的Docker镜像仓库