Lambda表达式和比较器的学习

前几天在写算法题的时候用到了优先队列这个数据结构, 当时还不太会用比较器和Lambda表达式, 正好趁此机会搞明白这两个东西的设计的来龙去脉和用法.

Lambda表达式正是比较器的一个很好的应用, 因为比较器是一个函数式接口, Lambda表达式就专门是为了简化函数式接口的使用而出现的.

一. Lambda表达式

问题背景

函数式接口:

就是一个类里面只有一个抽象方法, 这种类就叫函数式接口. 因为我们把这个接口的实现类对象当函数来用所以叫做函数式接口.

函数式接口的一般的使用流程:

  1. 创建一个实现类重写实现这个接口的方法
  2. new一个实现类的对象
  3. 将这个对象赋值给这个接口的引用变量, 变成一个引用
  4. 用这个接口引用去调用其唯一的方法---当作函数用

1. 匿名内部类

我们在上面的使用流程中发现有一步可以简化, 这一步就是第一步, 这一步我们要创建实现类, 但是正如我们问题背景所说, 我们单单是想把这个接口当函数用, 所以我们只关心这个函数式接口中的函数方法有没有实现, 是怎么实现的.

像是想实现类的名字这种东西我们完全可以简化掉, 由此产生了匿名内部类这个语法, 这就是我们对于函数式接口使用的第一次简化.

匿名内部类的使用方法:

  1. 我们现在有一个函数式接口, 假设叫做Runnable, Runnable里面有一个抽象方法叫run
  2. 现在我们要创建实现类重写这个抽象方法, 并把实现类对象赋给这个接口的引用变量
  3. 看看匿名内部类的语法是怎么做到的
Java 复制代码
Runnable task = new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("任务执行"); 
    } 
};
  1. 这个语法是新加的, 和以前的语法都不一样, 以前的语法是一个类我们可以在等式右边直接new出来, 并且输入初始化参数, 在创建对象的同时执行初始化方法. 匿名内部类是在右边加了{ }, 重写了一个抽象方法, 然后再new出来这个对象赋给左边接口引用, 这时因为没有给这个实现类命名所以叫做匿名内部类

2. Lambda表达式

Lambda表达式也是用来简化函数式接口的使用的, 函数式接口的使用流程中甚至1和2都可以简化, 因为我们只关心函数式接口的函数方法是怎么实现的. 所以我们借助了编译器的帮助进一步简化了函数式接口的使用.

函数式接口的使用方法:

  1. 同样现在有一个函数式接口, 假设叫做Runnable, Runnable里面有一个抽象方法叫run
  2. 现在我们要创建实现类重写这个抽象方法, 并把实现类对象赋给这个接口的引用变量
  3. 看看Lambda表达式的语法是怎么做到的
Java 复制代码
Runnable task = () -> System.out.println("任务执行");
  1. 首先编译器会判断当前位置引用变量是否为函数式接口的引用变量, 如果是便会用这个Lambda表达式重写他的函数方法, 函数方法原本有什么形参Lambda表达式中的形参类型和数量也要在( )中对应, 名字可以不一样. 然后把->后的作为函数体赋给函数方法, 最后创建这个实现类的对象赋给这个函数式接口的引用变量, 就可以通过这个引用来调用函数方法了.

注意Lambda表达式有一个语法细节:

1.单行表达式(无花括号 {}

自动返回表达式结果, 无需写 return, 返回的就是s1.length() - s2.length()的值

Java 复制代码
Comparator<String> byLength = (s1, s2) -> s1.length() - s2.length();

2.多行代码块(有花括号 {}

必须显式写 return 语句(如果有返回值)

Java 复制代码
Function<int[], Integer> sumOfSquares = arr -> {
    int sum = 0;
    for (int num : arr) {
        sum += num * num;
    }
    return sum;  // 必须显式返回
};

二. 比较器

比较器实际上就是一个函数式接口, 我们要实现比较器里面的函数方法, 所以前面的Lambda表达式是一个很常用的手段.比较器的比较函数进行了如下的约定, 用来判断你放入的两个元素谁排在前面, 谁排在后面.

函数方法输入(o1, o2)两个元素, 比较器的唯一核心规则

  • 返回负数 → 约定排序算法中o1 排在 o2 前面
  • 返回正数 → 约定排序算法中o2 排在 o1 前面
  • 返回0 → 顺序无关紧要

比较器的 compare(o1, o2) 方法本质是一个"裁判 ",它只返回一个整数(负 / 正 / 0),根据规则排序算法再操作:"o1o2 谁应该排在前面"

所以我们现在不管排序算法中的内容, 现在会用比较器的规则就行

  • 升序小的在前 → 应返回o1的属性 - o2的属性, 此时正负都是小的在前
  • 降序大的在前 → 应返回o2的属性 - o1的属性, 此时正负都是大的在前
  • 多条件:依次比较每个条件,直到返回非零结果。

我们先记住这个框架,遇到任何排序需求先能快速推导, 以后看排序算法的源码会有更深的理解.

相关推荐
典孝赢麻崩乐急3 分钟前
Java学习--------消息队列的重复消费、消失与顺序性的深度解析
java·开发语言·学习
汤姆yu1 小时前
基于springboot的考研互助小程序
java·spring boot·后端·考研互助
MoFe11 小时前
【.net core】支持通过属性名称索引的泛型包装类
java·开发语言·.netcore
没有bug.的程序员2 小时前
JAVA面试宝典 -《缓存架构:穿透 / 雪崩 / 击穿解决方案》
java·缓存·面试
Kiri霧2 小时前
Kotlin比较接口
android·java·前端·微信·kotlin
阿华的代码王国2 小时前
【Android】EditText使用和监听
android·xml·java
菜还不练就废了3 小时前
7.19 Java基础 | 异常
java·开发语言
Xxtaoaooo4 小时前
手撕Spring底层系列之:注解驱动的魔力与实现内幕
java·开发语言·后端开发·spring框架·原理解析
街霸星星4 小时前
使用 vfox 高效配置 Java 开发环境:一份全面指南
java
♛暮辞4 小时前
java程序远程写入字符串到hadoop伪分布式
java·hadoop·分布式