Java8 lambda函数式编程

时间:2022-03-09 19:00:24

Lambda 表达式是 Java SE8 推出的新功能,也是Java第一次引入函数式编程的尝试。

Lambda表达式格式
Lambda 表达式可以看做是一种匿名函数,但是它没有访问修饰符、返回值和名字。Lambda表达式由两部分构成,形式参数和方法体,中间用“->”符号分隔。其中的形式参数类型能够进行自动推断,可以不写。当然在某些特殊情况下,形参类型也是不可缺少的。方法体可以是简单的表达式或者代码块,下面是一些例子:

// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

要理解Lambda表达式,首先要了解一种特殊的接口:函数式接口。什么是函数式接口呢?简单来说就是只包含一个抽象方法的接口。Java标准库中的java.lang.Runnable和java.util.Comparator就是典型的函数式接口。对于函数式接口,我们就可以使用Lambda表达式来代替用传统匿名类来创建实例对象。
java.util.function包
在Java SE8之前标准库中的函数式接口并不多。JavaSE8增加了java.util.function包,里面都是可以在开发中只用的函数式接口。我们也可以自定义一个函数式接口,但最好在接口上使用@FunctionalInterface注解标明这是一个函数式接口,以免团队其它成员错误地往接口添加新的方法,当然java.util.function包中的所有接口都添加了@FunctionalInterface注解。
函数式接口

我们以Runnable接口为例,用传统匿名类的方式创建一个线程:

public void runThread() {
new Thread(new Runnable() {
public void run() {
System.out.println("Run!");
}
}).start();
}
上面传统匿名类方式中,我们可以看到,我们需要new一个接口名称,接口内部还要附带这个接口抽象方法的实现。而如果我们使用Lambda表达式,则代码非常简洁:

public void runThreadUseLambda() {
new Thread(() -> {
System.out.println("Run!");
}).start();
}

通过上面代码我们可以看到,Lambda表达式在两方面做了简化:

首先不需要声明Runnable接口,因为这可以通过上下文推断出来
其次不需要再写一个run方法的实现,因为函数式接口中只有一个方法
---------------------
使用场景

场景:有个User的List,需要将所有的用户ID取出来。
List<Integer> userIds = users.stream().map(u -> u.getId()).collect(Collectors.toList());
1
场景:有个User的List,需要将所有的用户ID取出来,还得注意去重
List<Integer> userIds = users.stream().map(u -> u.getId()).distinct().collect(Collectors.toList());
1
场景:有个User的List,需要将其放到HashMap里,key为用户ID,value为该User
Map<Integer, User> userMap = users.stream().collect(Collectors.toMap(User :: getId, u -> u));
1
场景:有个User的List,需要将其放到HashMap里,key为用户ID,value为用户名称
Map<Integer, String> userMap = users.stream().collect(Collectors.toMap(User :: getId, u -> u.getName()));
1
场景:有个User的List,需要将其放到HashMap里,key为用户ID,value为该User。User可能会重复,也就是ID会重复。按照上面的做法会导致如下错误:
Exception in thread "main" java.lang.IllegalStateException: Duplicate key 123456
1
/**
* 需要指定在key重复时选择保留旧值还是新值
*/
Map<Integer, User> userMap = users.stream().collect(Collectors.toMap(User :: getId, u -> u, (oldV, newV) -> newV));
1
场景:有个User的List,需要按照id的大小进行排序
List<User> users = new ArrayList<User>();
users.sort((u1, u2) -> u1.getId().compareTo(u2.getId()));
---------------------
常用方法

stream()、将集合变成了流,

filter、过滤到我们所需要的,

collect(Collectors.toList())、将得到的流对象转换为集合。 Collect()即为收集器用来将经过筛选、映射的流进行最后的整理

  groupingBy() 分组

  /joining()连接流中每个元素的toString方法生成的字符串

  /reducing()是针对单个值的收集,其返回结果不是集合家族的类型,而是单一的实体类T

  /collectingAndThen()转换函数返回的类型

  /Counting()计算流中个数/

  SummingInt()对流中元素的一个整数属性求和

  /averagingInt()计算流中元素integer属性的平均值

  maxBy()比较器选出的最大元素的optional,如果为空返回的是Optional.empty()

  minBy()比较器选出的最小元素的optional,如果为空返回的是Optional.empty()

  Collectors的常用封装有:toList()/toMap()/toCollection()/toSet()

map() 映射

distinct()去重

limit(3)截取流的前N个元素

skip(3)跳过流的前n个元素

equels() 比较

anyMatch()判断流中是否匹配任意元素

allMatch()判断流中是否匹配任意元素

noneMatch()判断流中是否未匹配所有元素

findAny()从流中获取任意元素,返回一个Optional类型的元素

    Optional是Java8新加入的一个容器,这个容器只存1个或0个元素,它用于防止出现NullpointException,它提供如下方法:

    isPresent()
    判断容器中是否有值。
    ifPresent(Consume lambda)
    容器若不为空则执行括号中的Lambda表达式。
    T get()
    获取容器中的元素,若容器为空则抛出NoSuchElement异常。
    T orElse(T other)
    获取容器中的元素,若容器为空则返回括号中的默认值。
    ---------------------
reduce()归约  是将集合中的所有元素经过指定运算,折叠成一个元素输出

           数值流采用reduce进行数值操作会涉及到基本数值类型和引用数值类型之间的装箱、拆箱操作,因此效率较低。 
           当流操作为纯数值操作时,使用数值流能获得较高的效率。

           StreamAPI提供了三种数值流:IntStream、DoubleStream、LongStream,也提供了将普通流转换成数值流的三种方法:mapToInt、mapToDouble、mapToLong。 

           每种数值流都提供了数值计算函数,如max、min、sum等

forEach()循环

sort()排序

sorted()排序

Comparator()排序

详细使用参考:https://blog.csdn.net/u010425776/article/details/52344425