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的属性, 此时正负都是大的在前
  • 多条件:依次比较每个条件,直到返回非零结果。

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

相关推荐
fish_xk几秒前
map和set
java·开发语言
李崧正15 分钟前
Java技术分享:Lambda表达式与函数式编程
java·开发语言·python
老了,不知天命17 分钟前
鳶尾花項目JAVA
java·开发语言·机器学习
二哈赛车手23 分钟前
新人笔记---实现简易版的rag的bm25检索(利用ES),以及RAG上传时的ES与向量数据库双写
java·数据库·笔记·spring·elasticsearch·ai
winner888127 分钟前
从零吃透C++命名空间、std、#include、string、vector
java·开发语言·c++
AI人工智能+电脑小能手35 分钟前
【大白话说Java面试题】【Java基础篇】第26题:Java的抽象类和接口有哪些区别
java·开发语言·面试
bzmK1DTbd44 分钟前
SOLID原则在Java中的实践:单一职责与开闭原则
java·开发语言·开闭原则
winner88811 小时前
C++ 命名空间、虚函数、抽象类、protected 权限全套通俗易懂精讲(附与 Java 对比)
java·开发语言·c++
直奔標竿1 小时前
Java开发者AI转型第二十五课!Spring AI 个人知识库实战(四)——RAG来源追溯落地,拒绝AI幻觉
java·开发语言·人工智能·spring boot·后端·spring
qq_589568101 小时前
java基础学习,案例练习,即时通讯
java·开发语言·学习