lamdba表达式 函数式接口 stream 整理
创始人
2024-02-13 13:40:13
0

lamdba表达式& 函数式接口& stream流 整理

lamdba,函数式接口(函数式编程) 是JDK8 的新特性,

  • 在开发中,可以使得代码更加的简洁;
  • 消除一定量的嵌套
  • 可读性提高(方法名称 fitler ,map .distinct ,count 等)
  • 大数据集合处理效率提升

java 的编程思想是一切都是对象,我们在写代码的时候也注重一个方法是有一个对象来完成的;

而函数式编程更加关注的是函数(方法)本身,关注是函数做了什么事情;

1.什么是lamdba表达式

官方释义:

A lambda expression is like a method: it provides a list of formal parameters and a body - an expression or block - expressed in terms of those parameters.
lambda表达式就像一个方法:它提供了一个形式参数列表和一个用这些参数表示的主体(表达式或块)。

lamdba 是JDK8 提供的语法糖,对匿名的内部类写法 进行 简化. 是函数式编程思想的一个体现

1.1 常用写法

格式: (参数列表)->{代码块} 其参数列表或者方法列表是推断出来的

下面就是一般的表达式的写法;

lambda表达式的求值产生函数接口的实例。Lambda表达式求值不会导致表达式正文的执行;相反,这可能在稍后调用函数接口的适当方法时发生。

//没有参数时的表达式 多行代码需要使用{{}}
() -> {}                // No parameters; result is void  没有参数 没有返回值(或者是返回值是void)
() -> 42                // No parameters, expression body   
() -> null              // No parameters, expression body
() -> { return 42; }    // No parameters, block body with return
() -> { System.gc(); }  // No parameters, void block body() -> {                 // Complex block body with returnsif (true) return 12;else {int result = 15;for (int i = 1; i < 10; i++)result *= i;return result;}
}                          //一个参数的时候 使用()包起来无所谓  
(int x) -> x+1              // Single declared-type parameter
(int x) -> { return x+1; }  // Single declared-type parameter
(x) -> x+1                  // Single inferred-type parameter
x -> x+1                    // Parentheses optional for// single inferred-type parameter(String s) -> s.length()      // Single declared-type parameter
(Thread t) -> { t.start(); }  // Single declared-type parameter
s -> s.length()               // Single inferred-type parameter
t -> { t.start(); }           // Single inferred-type parameter//要不全部声明参数类型 要不都不声明 声明部分在编译时会报错
(int x, int y) -> x+y  // Multiple declared-type parameters
(x, y) -> x+y          // Multiple inferred-type parameters
(x, int y) -> x+y    // Illegal: can't mix inferred and declared types
(x, final y) -> x+y  // Illegal: no modifiers with inferred types

案例一 :

// 会自动推导出类型是Runnable 方法体是run 方法
new Thread(() -> {System.out.println("start");}).start();new Thread(new Runnable() {@Overridepublic void run() {System.out.println("start");}}).start();//可以看到 Runnable 是一个函数式接口  
@FunctionalInterface
public interface Runnable {public abstract void run();
}

1.2.lamdba 表达式的参数

lamdba 表达式的参数要不是声明好类型,要不是推断的类型,但是不能混用(多个参数,声明其中部分类型)

  • 声明参数类型:lambda表达式称为显式类型
  • 不声明参数类型:lambda表达式称为隐式类型

零参数的lambda表达式是显式类型

参数类型声明的时候注意:

  • 要不全部声明参数类型 要不都不声明 声明部分在编译时会报错
  • 一个参数的时候 使用()包起来无所谓

1.2 lamdba 表达式的方法体

单个表达式或块。与方法体一样,lambda体描述每当调用发生时将执行的代码;

//void 类型返回
() -> {}
() -> { System.out.println("done"); }// 有返回值
() -> { return "done"; }
() -> { if (...) return 1; else return 0; }

lambda表达式中使用但未声明的任何局部变量、形式参数或异常参数必须声明为final或有效final,否则在尝试使用时发生编译时错误;

: 什么是有效final ? 也叫efftive final ; 他没有final 显示的修饰,并且后面不会对它二次赋值(官方文档的描述是是不在赋值预算符的左侧,或者是递增递减的前缀和后缀[++ – ]); 并且已经初始化

1.3 lamdba 表达式的数据类型

如果 T 是一个函数接口类型;,并且该表达式与从 T 派生的地面目标类型的函数类型一致,则 lambda 表达式在赋值上下文、调用上下文或强制转换上下文中与目标类型 T 兼容。

目标类型由 T 推导如下:

如果 T 是通配符参数化的函数接口类型,并且 lambda 表达式是显式类型的,那么将按照描类型描述述断目标类型。

如果 T 是一个通配符参数化的函数接口类型,而 lambda 表达式是隐式类型的,那么 ground 目标类型是 t 的非通配符参量化;

2.函数式接口

函数式接口:任何接口,如果只包含唯一一个抽象方法(除了 Object 的 public 方法之外) ,那么它就是一个函数式接口。

2.1 案例判断

//是
interface Runnable {void run();
}
//不是
interface NonFunc {boolean equals(Object obj); //Object 方法
}//是的
interface Func extends NonFunc {int compare(String o1, String o2); //唯一的 非object 的一个抽象方法
}//所以类似 java.util.Comparator 是一个函数式接口
interface Comparator {boolean equals(Object obj); // Object 方法int compare(T o1, T o2);
}//两个接口  clone 是Object 的非 public 的
interface Foo {int m();Object clone();
}

如果有两个方法(FI5 ) ,

  • m 的签名是 M 中每个方法签名的子签名
  • m 方法返回值是可以替代类型 如 FI2.m 的方法返回值签名Iterable 可以替代 FI3.m() 的 Iterable
		@FunctionalInterfacepublic interface  FI2  {Iterable m(Iterable arg);}@FunctionalInterfacepublic interface  FI3 {Iterable m(Iterable arg);}@FunctionalInterface  // 也是一个函数式接口interface FI5 extends FI2, FI3 { /  /这个也是  FI2.m 的方法签名可以替代 Iterable}

注意:@FunctionalInterface 作用,用于编译器校验你的接口是不是函数式接口

2.2 实例化

对于函数式接口:

  • 通过声明和实例化类(15.9)来创建接口实例的常规过程

  • 还可以使用方法引用表达式和 lambda 表达式来创建函数接口实例。

    public static void main(String[] args) {//常规过程FI fi = new FIDemo();fi.fun1();// lambda 表达式fi = ()-> System.out.println("FIDemo2");fi.fun1();}static class FIDemo implements FI{@Overridepublic void fun1() {System.out.println("FIDemo1");}}public interface  FI {void fun1();}

2.3 常用的函数式接口

java.util.function 包下面的四大种类

 				//供给型 生产型   没有参数  有输出的  Supplier sup = () -> {return 100;};//消费型 有输入 没有输出的  消费型(消费输入)Consumer consumer = (x) -> {System.out.println(" return void" + x);};//有输入也有输出的  函数型接口 (第一个泛型是输入,第二个泛型是输出)  计算转换类型Function function = (x)-> x.length();//判断型接口Predicate predicate = (x)->{boolean b = x.length() > 5;return b;};

3.Stream流

JDK8 的Stream 使用函数式编程模式,可以使我们对集合的操作就像对流一样链式操作;简化和方便对 集合的操作;

流的操作可以氛围三大部分

  • 获取流,
  • 流中间操作
  • 输出流中数据 也就是终结流

3.1 Stream流的创建/获取

//链表获取流
List users = getUsers();
Stream stream = users.stream();//数组获取流
Integer[] arr = {1,3,5,7,9};
Stream stream1 = Arrays.stream(arr);//map 可以转entryset 转流

3.2 中间操作

中间的操作是对流中数据操作,方法返回的还是Stream 意味着后面还是可以接着中间操作的函数

数据准备

    public static List getUsers() {List users = new ArrayList<>();users.add(new User(1, "孙菲菲", 4, "杨浦区"));users.add(new User(1, "孙菲菲", 4, "杨浦区"));users.add(new User(2, "熊大", 5, "青青草原"));users.add(new User(3, "熊二", 10, "青青草原"));return users;}

1.filter

数据过滤

Stream filter(Predicate predicate);public static void filter() {List users = getUsers();users.stream().filter(x -> x.getId() > 1)  //中间是匿名函数 Predicate(结合2.3) 的实现 是一个判断条件.forEach(x -> System.out.println(x));}//输出
User(id=2, name=熊大, age=5, address=青青草原)
User(id=3, name=熊二, age=10, address=青青草原) 

2.map

数据转换

 Stream map(Function mapper);public static void map() {List users = getUsers();users.stream().map(x -> {return "银河系,地球村 :" + x.getAddress();}) //中间是匿名函数 Function(结合2.3) 的实现 y有输入和输出, 输入是User 对象 输出是 String.forEach(x -> System.out.println(x));}//输出
银河系,地球村 :杨浦区
银河系,地球村 :杨浦区
银河系,地球村 :青青草原
银河系,地球村 :青青草原

3.distinct

数据去重

 Stream distinct();  public static void distinct() {List users = getUsers();users.stream().distinct() //依赖于Object 的equals 方法来实现.forEach(x -> System.out.println(x));}

4.sorted

排序

Stream sorted();  
Stream sorted(Comparator comparator);public static void sorted() {List users = getUsers();users.stream().sorted().forEach(x -> System.out.println(x));}//报错
Exception in thread "main" java.lang.ClassCastException: com.common.util.User cannot be cast to java.lang.Comparable两个方法方法1:User 实现 Comparable 接口方法2:sorted(Comparator comparator) 使用带参数的方法二实现
public static void sorted() {List users = getUsers();users.stream().sorted(Comparator.comparingInt(User::getAge))  //sorted() 使用带参数的.forEach(x -> System.out.println(x));
}

5.limit

截取前面几条数据

 Stream limit(long maxSize);public static void limit() {List users = getUsers();users.stream().limit(3).forEach(x -> System.out.println(x));}//输出
User(id=1, name=孙菲菲, age=4, address=杨浦区)
User(id=1, name=孙菲菲, age=4, address=杨浦区)
User(id=2, name=熊大, age=5, address=青青草原)

6.skip

跳过前面几条数据

Stream skip(long n);public static void skip() {List users = getUsers();users.stream().skip(2).forEach(x -> System.out.println(x));}User(id=2, name=熊大, age=5, address=青青草原)
User(id=3, name=熊二, age=10, address=青青草原)

7.flatMap

map 对象是一对一转出 , flatMap 可以一对多转出对象

 Stream flatMap(Function> mapper)public static void flatMap() {List users = getUsers();users.stream().flatMap((Function>) user -> Arrays.asList(user.getId(), user.getAddress()).stream()) //flatMap 内是一个Function ,Function 的输出是一个 子流(可以这样理解,每个元素拆分的流).forEach(x -> System.out.println(x));}//输出
1
杨浦区
1
杨浦区
2
青青草原
3
青青草原

3.3 流的终结操作

1.foreach

遍历流中的数据

    public static void foreach() {List users = getUsers();users.stream().forEach(x -> System.out.println(x));}//输出
User(id=1, name=孙菲菲, age=4, address=杨浦区)
User(id=1, name=孙菲菲, age=4, address=杨浦区)
User(id=2, name=熊大, age=5, address=青青草原)
User(id=3, name=熊二, age=10, address=青青草原)

2.count

计算流中的数据

    public static void count() {List users = getUsers();long count = users.stream().filter(x -> x.getId() > 1).count();System.out.println(count);}//输出
2

3.max&min

计算流中最大值,最小值

public static void max() {List users = getUsers();Optional max = users.stream().filter(x -> x.getId() > 1).max(Comparator.comparingInt(User::getAge));//.min(Comparator.comparingInt(User::getAge));System.out.println(max.get()); //getAge 最大的
}User(id=3, name=熊二, age=10, address=青青草原)

4.collect

流中的元素转变成集合

    public static void collect() {List users = getUsers();List list = users.stream().map(x -> x.getId()).collect(Collectors.toList());System.out.println(list);System.out.println("=========");Set set = users.stream().map(x -> x.getId()).collect(Collectors.toSet());System.out.println(set);System.out.println("=========");Map nameToUser = users.stream().collect(Collectors.toMap(User::getName, Function.identity()));System.out.println(nameToUser);System.out.println("========="); //这里会报错 Exception in thread "main" java.lang.IllegalStateException: Duplicate key User(id=1, name=孙菲菲, age=4, address=杨浦区)//需要先去重 或者下面方法 重复使用哪一个//(x, x1) -> x 使用前一个//(x, x1) -> x1 使用后一个Map nameToUser = users.stream().collect(Collectors.toMap(User::getName, Function.identity(), (x, x1) -> x1));System.out.println(nameToUser);System.out.println("=========");Map> nameToUserList = users.stream().collect(Collectors.groupingBy(User::getName));System.out.println(nameToUserList);System.out.println("=========");}

5.查询匹配

  • anyMatch:是否有任意一个满足
  • allMatch:是否所有元素都满足
  • noneMatch: 是否都不满住
    public static void matchAndSelect() {List users = getUsers();boolean b = users.stream().map(x -> x.getAge()).allMatch(x -> x > 10);System.out.println("所有age>10:" + b);b = users.stream().map(x -> x.getAge()).anyMatch(x -> x > 5);System.out.println("任意age>5:" + b);b = users.stream().map(x -> x.getAge()).noneMatch(x -> x > 10);System.out.println("没有一个age > 10:" + b);}
//元素users.add(new User(1, "孙菲菲", 4, "杨浦区"));users.add(new User(1, "孙菲菲", 4, "杨浦区"));users.add(new User(2, "熊大", 5, "青青草原"));users.add(new User(3, "熊二", 10, "青青草原"));所有age>10:false
任意age>5:true
所有都<=10:true
  • findAny :或取流中任意一个;(并行流中,任意一个流的第一个,串行流都是一样)
  • findFirst:获取流中第一个
    public static void select() {List users = getUsers();Optional any = users.stream().findAny();System.out.println(any);Optional first = users.stream().findFirst();System.out.println(first);}//输出
Optional[User(id=1, name=孙菲菲, age=4, address=杨浦区)]
Optional[User(id=1, name=孙菲菲, age=4, address=杨浦区)]

6.reduce

对流中数据进行指定计算方式运行出结果( 聚合,缩减)

比如说求和

    public static void reduce() {List users = getUsers();Integer reduce = users.stream().map(x -> x.getAge()).reduce(0, (integer, integer2) -> integer + integer2);System.out.println(reduce);}	

3.4 并行流

大量数据的时候,可以使用并行流处理数据.

并行流的底层是把任务分配给多个线程(ForkJoinn Pool 线程池)去执行.

    public static void parallelStream() {List users = getUsers();Integer reduce = users.parallelStream().peek(x->{System.out.println(Thread.currentThread().getName());}).map(x -> x.getAge()).reduce(0, (integer, integer2) -> integer + integer2);System.out.println(reduce);}//输出
ForkJoinPool.commonPool-worker-9
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-11
main
23

3.5 注意

  • 流是一次性的 使用过(被终结操作终结过的 就不能在使用了)
  • 流的操作一般不会影响原来数据地址(集合元素的地址)

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...