Lambda表达式

文章目录

  • 函数式编程
    • 示例(累加)
    • 示例(斐波那契数列)
  • 纯函数
  • 不可变计算
    • 如何建立不可变对象
  • Lambda 表达式
    • Lambda表达式的格式
    • Lambda表达式的省略模式
    • Lambda表达式的注意事项
    • Lambda表达式和匿名内部类的区别
    • Lambda表达式支持方法引用
  • 函数式接口
    • 常用的函数式接口
  • Stream
    • 特点
    • 流程
    • 中间操作
    • 终端操作符

java8中一个非常重要的特性就是lambda表达式,可以把它看成是一种闭包,它允许把函数当做参数来使用,是面向函数式编程的思想,一定程度上可以使代码看起来更加简洁。

函数式编程

函数式编程将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。

函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。

和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里函数的计算可随时调用。

示例(累加)

使用java实现累加。

import java.util.Arrays;
import java.util.List;

class Accumulation {
    public static Integer sum(List<Integer> nums) {
        int result = 0;
        for (Integer num : nums) {
            result += num;
        }

        return result;
    }

    public static void main(String[] args) {
        List<Integer> nums = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        System.out.println(sum(nums)); // 55
    }
}

同样的代码,用 Java8 Stream 实现。

Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
	.stream()
	.reduce(0, Integer::sum);

示例(斐波那契数列)

java实现。

class Fibonacci {
    public static int fibonacci(int number) {
        if (number == 1) {
            return 1;
        }
        if(number == 2) {
            return 2;
        }

        int a = 1;
        int b = 2;
        for(int cnt = 3; cnt <= number; cnt++) {
            int c = a + b;
            a = b;
            b = c;
        }
        return b;
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            System.out.print(fibonacci(i) + ", ");
        }
    }
}
// 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 

用 Java8 Stream 实现。

Stream.iterate(new int[]{1, 1}, s -> new int[]{s[1], s[0] + s[1]})
	.limit(10)
	.map(n -> n[1])
	.collect(Collectors.toList())
// [1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

纯函数

纯函数是指执行过程中没有副作用的函数,所谓副作用是说超出函数控制的操作,比如在执行过程中操作文件系统、数据库等外部资源。

纯函数还具有引用透明性的特点,也就是同样的输入导致同样的输出,以至于完全可以用函数的值代替对函数的调用。

不可变计算

一般来说,有两种改变数据的方式。第一种方式是直接修改变量的值,第二种方式是使用新的一份数据替换旧数据。

不可变对象(Immutable Object):对象一旦被建立后,对象全部的状态及属性在其生命周期内不会发生任何变化。

谈到不可变性,我们做个游戏:统计在座的人数。

我们都知道从某个人开始依次报数,最后得到的数字就是总人数,其实这就是一种不可变计算的游戏,为什么这么说呢?因为报数其实一个计算的过程,第一个人计算出1这个数,传递给第二个人。然后第二个人拿着前面的1进行加一操作,然后把结果2传递给后面的人做加法,以此类推。

为了提高统计的效率,我也可以进行分组,然后每组自行报数,最后统计结果。

但是如果我在白板上写个数字1,然后让大家过来改这个数字,很大可能会出现错误,因为这个数字成为了竞争条件。在多并发的情况下,就得用读写锁来控制。所以不可变性特别利于并发。

如何建立不可变对象

一般来讲,建立不可变类原则有如下几条:

  1. 全部成员变量必须是private
  2. 最好同时用final修饰(非必须)
  3. 不提供可以修改原有对象状态的方法
    • 最多见的方式是不提供setter方法
    • 若是提供修改方法,须要新建立一个对象,并在新建立的对象上进行修改
  4. 经过构造器初始化全部成员变量,引用类型的成员变量必须进行深拷贝(deep copy)
  5. getter方法不能对外泄露this引用以及成员变量的引用
  6. 最好不容许类被继承(非必须)

Lambda 表达式

Lambda Calculus 在数学定义上面非常简单,并且和与图灵机等价,也就是说FP语言程序与普通的命令式语言如C可以写出同样能力的程序。lambda算子理论一切都是函数。

Lambda演算在编程语言之中是一个编程范式,它遵循如下几个特点:

  1. 数据的不可变性,任何对于数据的操作没有副作用。
  2. 数据的无依赖性,即对函数提供同样的输入,那么函数总是返回同样的结果。
  3. 函数是一等公民,函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传给另一个函数,或者作为别的函数的返回值。

Java 8开始引入 Lambda 表达式,使代码更加简洁。

Lambda 表达式包含三个要素:

  • 变量
  • lambda 抽象
  • lambda 应用

当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。

没有使用Lambda:

button.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent actionEvent){
        System.out.println("Action detected");
    }
});

使用Lambda:

button.addActionListener( actionEvent -> { 
    System.out.println("Action detected");
});

让我们来看一个更明显的例子。

不采用Lambda:

Runnable runnable1 = new Runnable(){
	@Override
	public void run(){
    	System.out.println("Running without Lambda");
	}
};

使用Lambda:

Runnable runnable2 = ()->System.out.println("Running from Lambda");

从示例可以看到,使用Lambda表达式不仅让代码变得简单,而且可读,最重要的是代码量也随之减少很多。

Lambda表达式的格式

(形式参数)-> { 代码块 }

  • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
  • ->:由英文连字符和大于符号组成,固定写法。代表指向动作
  • 代码块:是具体要做的事情,也就是以前写的方法体

Lambda表达式的省略模式

省略规则:

  • 参数类型可以省略。如果有多个参数的情况下,不能只省略一个
  • 如果参数有且仅有一个,那么小括号可以省略
  • 如果代码块的语句只有一条,可以省略大括号和分号,甚至时return

Lambda表达式的注意事项

注意事项:

  • 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象的方法
  • 必须有上下文环境,才能推导出Lambda对应的接口
    • 根据局部变量的赋值得知Lambda对应的接口:Runnable r =() ->System.out.println(“Lambda表达式”);
    • 根据调用方法的参数得知Lambda对应的接口:new Thread(()->System.out.println(“Lambda表达式”)).start();

Lambda表达式和匿名内部类的区别

所需类型不同:

  • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
  • Lambda表达式:只能是接口

使用限制不同:

  • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
  • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

实现原理不同:

  • 匿名内部类:编译之后,产生一个单独的.class字节码文件
  • Lambda表达式:编译之后,没有一个单独的.class字节码文件,对应的字节码会在运行的时候动态生成

Lambda表达式支持方法引用

常见的引用方式:

  • 引用类方法(指类的静态方法)
  • 引用对象的实例方法(成员方法,使用对象来引用)
  • 引用类的实例方法(成员方法,使用类来引用)
  • 引用构造器(构造方法,使用new来引用)

函数式接口

函数式接口:有且仅有一个抽象方法的接口。

Java中的函数式编程体现就是Lambda表达式,所以函数式接口就可以使用于Lambda使用的接口。

只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

如何检测一个接口是不是函数式接口呢?

  • @FunctionalInterface
  • 放在接口定义的上方:如果接口是函数接口,编译通过;如果不是,编译失败
  • @FunctionalInterface是可选的,就算不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是,建议加上注解。

常用的函数式接口

Java 8 在java.util.function包下预定了大量的函数式接口,常用如下:

  • Supplier接口
    • 称为生产型接口,如果指定了接口的泛型是什么类型,接口中的get方法就会生产什么类型的数据。
  • Consumer接口
    • 称为消费型接口,它消费的数据类型由泛型指定。
  • Predicate接口
    • 判断接口,可以对给定参数进行判断或进行逻辑判断。
  • Function接口
    • 通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现)然后返回一个新的值。

Stream

Stream是Java 8 API添加的一个新的抽象,称为流Stream,以一种声明性方式处理数据集合(侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式)。

Stream流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算。

Stream流是对集合(Collection)对象功能的增强,与Lambda表达式结合,可以提高编程效率、间接性和程序可读性。

特点

  1. 代码简洁:函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环。
  2. 多核友好:Java函数式编程使得编写并行程序更简单,就是调用对应的并行方法。

流程

  1. 将集合转换为Stream流(或者创建流)。
  2. 操作Stream流(中间操作,终端操作)。

stream流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

中间操作

对Stream的中间操作,可以视为是源的查询,并且是惰式设计,对于源数据进行的计算只有在需要时才会被执行,与数据库中视图的原理相似。

Stream流的强大之处在于提供了丰富的中间操作,相比集合或数组这类容器,极大的简化源数据的计算复杂度。

一个流可以跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。

这类操作都是惰性的,仅仅调用到这类方法,并没有真正开始流的遍历,真正的遍历需等到终端操作时,常见的中间操作有 filter、map 等。

终端操作符

Stream流执行完终端操作之后,无法再执行其他动作,否则会报状态异常,提示该流已经被执行操作或者被关闭,想要再次执行操作必须重新创建Stream流。

一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据再生成流。

终端操作的执行,才会真正开始流的遍历。如 count、collect 等。

Stream代码示例如下:

import java.util.*;
import java.util.stream.Collectors;

public class StreamTests {
    public static void main(String[] args) {
        List<User> userList = getUserList();

        // filter:输出ID大于6的user对象
        // collect:收集器,将流转换为其他形式
        System.out.println("----------filter--------");
        List filetrUserList = userList.stream()
                .filter(user -> user.getId() > 6l)
                .collect(Collectors.toList());
        // forEach:遍历流
        filetrUserList.forEach(System.out::println);

        // map 映射成一个新的元素
        System.out.println("-------map------------");
        List mapUserList = userList.stream()
                .map(user -> user.getUsername() + "用户")
                .collect(Collectors.toList());
        mapUserList.forEach(System.out::println);

        // distinct:去重
        System.out.println("-------distinct------------");
        List distinctUsers =  userList.stream()
                .map(User::getCity)
                .distinct()
                .collect(Collectors.toList());
        distinctUsers.forEach(System.out::println);

        // sorted:排序,根据名字倒序
        System.out.println("-------sorted------------");
        userList.stream()
                .sorted(Comparator.comparing(User::getUsername).reversed())
                .collect(Collectors.toList())
                .forEach(System.out::println);

        //  limit:取前5条数据
        System.out.println("-----limit--------");
        userList.stream()
                .limit(5)
                .collect(Collectors.toList())
                .forEach(System.out::println);

        // skip:跳过第几条取后几条
        System.out.println("--------skip--------");
        userList.stream()
                .skip(7)
                .collect(Collectors.toList())
                .forEach(System.out::println);

        // flatMap:数据拆分一对多映射
        System.out.println("--------flatMap--------");
        userList.stream()
                .flatMap(user -> Arrays.stream(user.getCity().split(",")))
                .forEach(System.out::println);

        // peek:对元素进行遍历处理,每个用户ID加1输出
        System.out.println("--------peek--------");
        userList.stream()
                .peek(user -> user.setId(user.getId() + 1))
                .forEach(System.out::println);

        // findFirst:返回第一个元素
        System.out.println("--------findFirst--------");
        System.out.println(userList.stream()
                .findFirst()
                .get());
        System.out.println("--------findFirst--------");
        System.out.println(userList.stream()
                .filter(user -> "深圳".equals(user.getCity()))
                .findFirst()
                .get());

        // findAny:将返回当前流中的任意元素
        System.out.println("--------findAny--------");
        System.out.println(userList.stream()
                .findAny()
                .get());
        System.out.println("--------findAny--------");
        System.out.println(userList.stream()
                .filter(user -> "深圳".equals(user.getCity()))
                .findAny()
                .get());

        // count:返回流中元素总数
        System.out.println("--------count--------");
        long count = userList.stream()
                .filter(user -> user.getAge() > 20)
                .count();
        System.out.println(count);

        // sum:求和
        System.out.println("--------sum--------");
        long sum = userList.stream()
                .mapToLong(User::getId)
                .sum();
        System.out.println(sum);

        // max:最大值
        System.out.println("--------max--------");
        long max = userList.stream()
                .max(Comparator.comparingLong(User::getId))
                .get()
                .getId();
        System.out.println(max);

        // min:最小值
        System.out.println("--------min--------");
        long min = userList.stream()
                .min(Comparator.comparingLong(User::getId))
                .get()
                .getId();
        System.out.println(min);

        // anyMatch:检查是否至少匹配一个元素
        System.out.println("--------anyMatch--------");
        boolean matchAny = userList.stream()
                .anyMatch(user -> "北京".equals(user.getCity()));
        System.out.println(matchAny);

        // allMatch:检查是否匹配所有元素
        System.out.println("--------matchAll--------");
        boolean matchAll = userList.stream()
                .allMatch(user -> "北京".equals(user.getCity()));
        System.out.println(matchAll);

        // noneMatch:检查是否没有匹配所有元素,返回boolean
        System.out.println("--------noneMatch--------");
        boolean nonaMatch = userList.stream()
                .allMatch(user -> "云南".equals(user.getCity()));
        System.out.println(nonaMatch);

        // reduce:将流中元素反复结合起来,得到一个值
        System.out.println("--------findAny--------");
        Optional reduce = userList.stream()
                .reduce((user, user2) -> { return user; });
        if(reduce.isPresent())
            System.out.println(reduce.get());

        // Collectors.toList 将用户ID存放到List集合中
        System.out.println("--------Collectors.toList--------");
        List idList = userList.stream()
                .map(User::getId)
                .collect(Collectors.toList());
        System.out.println(idList);

        // Collectors.toMap 将用户ID和username以Key-Value形式存放到Map集合中
        System.out.println("--------Collectors.toMap--------");
        Map<Long, String> userMap = userList.stream()
                .collect(Collectors.toMap(User::getId, User::getUsername));
        System.out.println(userMap);

        // Collectors.toSet 将用户所在城市存放到Set集合中
        System.out.println("--------Collectors.toSet--------");
        Set citySet = userList.stream()
                .map(User::getCity)
                .collect(Collectors.toSet());
        System.out.println(citySet);

        // Collectors.counting 符合条件的用户总数
        System.out.println("--------Collectors.counting--------");
        long count1 = userList.stream()
                .filter(user -> user.getId()>1)
                .collect(Collectors.counting());
        System.out.println(count1);

        // Collectors.summingLong 对结果元素即用户ID求和
        System.out.println("--------Collectors.summingLong--------");
        Long sumLong = userList.stream()
                .filter(user -> user.getId()>2)
                .collect(Collectors.summingLong(User::getId));
        System.out.println(sumLong);

        // Collectors.minBy 筛选元素中ID最小的用户
        System.out.println("--------Collectors.minBy--------");
        User minId = userList.stream()
                .collect(Collectors.minBy(Comparator.comparingLong(User::getId)))
                .get();
        System.out.println(minId);

        // Collectors.joining 将用户所在城市,以指定分隔符链接成字符串;
        System.out.println("--------Collectors.joining--------");
        String joinCity = userList.stream()
                .map(User::getCity)
                .collect(Collectors.joining("||"));
        System.out.println(joinCity);

        // Collectors.groupingBy 按条件分组,以城市对用户进行分组;
        System.out.println("--------Collectors.groupingBy--------");
        Map<String,List<User>> groupCity = userList.stream()
                .collect(Collectors.groupingBy(User::getCity));
        System.out.println(groupCity);
    }

    private static List<User> getUserList() {
        List<User> userList = new ArrayList<>();

        userList.add(new User(1l,"Mary",18,"上海"));
        userList.add(new User(2l,"John",16,"上海"));
        userList.add(new User(3l,"Jerry",20,"上海"));
        userList.add(new User(4l,"Siri",22,"北京"));
        userList.add(new User(5l,"Mini",15,"深圳"));
        userList.add(new User(6l,"Marks",24,"北京"));
        userList.add(new User(7l,"Aili",21,"上海"));
        userList.add(new User(8l,"Baidu",88,"广州"));
        userList.add(new User(9l,"Jin",76,"广州"));
        userList.add(new User(10l,"Yi",26,"深圳"));

        return userList;
    }
}

class User {
    private Long id;
    private String username;
    private Integer age;
    private String city;

    public User(Long id, String username, Integer age, String city) {
        this.id = id;
        this.username = username;
        this.age = age;
        this.city = city;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("{");
        sb.append("\"id\":")
                .append(id);
        sb.append(", \"username\":\"")
                .append(username).append('\"');
        sb.append(", \"age\":")
                .append(age);
        sb.append(", \"city\":\"")
                .append(city).append('\"');
        sb.append('}');
        return sb.toString();
    }
}

结果如下

----------filter--------
{"id":7, "username":"Aili", "age":21, "city":"上海"}
{"id":8, "username":"Baidu", "age":88, "city":"广州"}
{"id":9, "username":"Jin", "age":76, "city":"广州"}
{"id":10, "username":"Yi", "age":26, "city":"深圳"}
-------map------------
Mary用户
John用户
Jerry用户
Siri用户
Mini用户
Marks用户
Aili用户
Baidu用户
Jin用户
Yi用户
-------distinct------------
上海
北京
深圳
广州
-------sorted------------
{"id":10, "username":"Yi", "age":26, "city":"深圳"}
{"id":4, "username":"Siri", "age":22, "city":"北京"}
{"id":5, "username":"Mini", "age":15, "city":"深圳"}
{"id":1, "username":"Mary", "age":18, "city":"上海"}
{"id":6, "username":"Marks", "age":24, "city":"北京"}
{"id":2, "username":"John", "age":16, "city":"上海"}
{"id":9, "username":"Jin", "age":76, "city":"广州"}
{"id":3, "username":"Jerry", "age":20, "city":"上海"}
{"id":8, "username":"Baidu", "age":88, "city":"广州"}
{"id":7, "username":"Aili", "age":21, "city":"上海"}
-----limit--------
{"id":1, "username":"Mary", "age":18, "city":"上海"}
{"id":2, "username":"John", "age":16, "city":"上海"}
{"id":3, "username":"Jerry", "age":20, "city":"上海"}
{"id":4, "username":"Siri", "age":22, "city":"北京"}
{"id":5, "username":"Mini", "age":15, "city":"深圳"}
--------skip--------
{"id":8, "username":"Baidu", "age":88, "city":"广州"}
{"id":9, "username":"Jin", "age":76, "city":"广州"}
{"id":10, "username":"Yi", "age":26, "city":"深圳"}
--------flatMap--------
上海
上海
上海
北京
深圳
北京
上海
广州
广州
深圳
--------peek--------
{"id":2, "username":"Mary", "age":18, "city":"上海"}
{"id":3, "username":"John", "age":16, "city":"上海"}
{"id":4, "username":"Jerry", "age":20, "city":"上海"}
{"id":5, "username":"Siri", "age":22, "city":"北京"}
{"id":6, "username":"Mini", "age":15, "city":"深圳"}
{"id":7, "username":"Marks", "age":24, "city":"北京"}
{"id":8, "username":"Aili", "age":21, "city":"上海"}
{"id":9, "username":"Baidu", "age":88, "city":"广州"}
{"id":10, "username":"Jin", "age":76, "city":"广州"}
{"id":11, "username":"Yi", "age":26, "city":"深圳"}
--------findFirst--------
{"id":2, "username":"Mary", "age":18, "city":"上海"}
--------findFirst--------
{"id":6, "username":"Mini", "age":15, "city":"深圳"}
--------findAny--------
{"id":2, "username":"Mary", "age":18, "city":"上海"}
--------findAny--------
{"id":6, "username":"Mini", "age":15, "city":"深圳"}
--------count--------
6
--------sum--------
65
--------max--------
11
--------min--------
2
--------anyMatch--------
true
--------matchAll--------
false
--------noneMatch--------
false
--------findAny--------
{"id":2, "username":"Mary", "age":18, "city":"上海"}
--------Collectors.toList--------
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
--------Collectors.toMap--------
{2=Mary, 3=John, 4=Jerry, 5=Siri, 6=Mini, 7=Marks, 8=Aili, 9=Baidu, 10=Jin, 11=Yi}
--------Collectors.toSet--------
[上海, 广州, 北京, 深圳]
--------Collectors.counting--------
10
--------Collectors.summingLong--------
63
--------Collectors.minBy--------
{"id":2, "username":"Mary", "age":18, "city":"上海"}
--------Collectors.joining--------
上海||上海||上海||北京||深圳||北京||上海||广州||广州||深圳
--------Collectors.groupingBy--------
{广州=[{"id":9, "username":"Baidu", "age":88, "city":"广州"}, {"id":10, "username":"Jin", "age":76, "city":"广州"}], 上海=[{"id":2, "username":"Mary", "age":18, "city":"上海"}, {"id":3, "username":"John", "age":16, "city":"上海"}, {"id":4, "username":"Jerry", "age":20, "city":"上海"}, {"id":8, "username":"Aili", "age":21, "city":"上海"}], 深圳=[{"id":6, "username":"Mini", "age":15, "city":"深圳"}, {"id":11, "username":"Yi", "age":26, "city":"深圳"}], 北京=[{"id":5, "username":"Siri", "age":22, "city":"北京"}, {"id":7, "username":"Marks", "age":24, "city":"北京"}]}

java的lambda、链式调用、函数式编程、stream流都是非常有用的,在代码中经常都会遇到,可以提高生产力,让程序员写出高效率、干净、简洁的代码。

你可能感兴趣的:(开发技术,java)