一、lamdba表达式基础知识
1、lamdba基本知识
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
2、lamdba的优点、为什么要是使用lamdba
在Java中,如果不使用lambda,那么恐怕就只能选择用匿名内部类来应付类似的场景。
首先看以下这个例子:
class Foobar {
Runnable foo() {
return new Runnable() {
@Override
public void run() {
}
};
}
Runnable bar() {
return () -> {};
}
}
同样的功能,两种写法,但是结果并不是相同的。
首先,第一种是一个非静态匿名内部类,这样的一个类它会在编译期额外定义一个类,这个类的每个实例都将会持有外部实例的强引用。
这样的结果就会导致,如果这个匿名类的实例没有被销毁,那么外部类的实例也不能够被销毁,于是就有可能发生内存泄漏,比如当某Handler持有Activity的强引用并且没有被GC回收时,结果导致Activity即使destroy之后也不能被GC回收,这就是一种典型的内存泄漏的场景。
而对于上面这个lambda来说,由于它并没有捕获this,因此它并不会持有this的引用,即使经过了Desuger,仍旧不会捕获this的强引用,所以不会因此而发生内存泄漏。
其次,对于上面这种情况来说,由于没有任何变量需要被捕获,因此每次调用实际上根本没有必要重复创建新的对象,而用lambda就可以做到这一点,上面这个lambda每次返回的都是同一个实例的引用。
最后,相比起以上两条来说,这一条显得无关紧要。由于匿名类也是类,所以它经过编译后也要有自己的class文件,而在每个class文件中的常量表都会占据相当一部分空间,而如果是用lambda代替匿名类,则不会在编译期生成这些class文件,于是二进制文件占据的空间会稍微减少,不过如果是在android开发中,这种区别将会变得更加微不足道,因为dex已经为了这种情况而节省了大量的空间出来,dex数量远远小于class数量因此常量表也会被更多地复用,所以lambda与匿名类在包大小上的区别实际并不明显。
3、lamdba的基本语法
()->{}
语法精简的规则,可以看下面
4、什么时候使用
二、lamdba实战
1、创建接口
@FunctionalInterface public interface lamdbaInterface1 { } 作用:只允许该接口有一个实现方法,(因为jdk1.8新特性,出现了default关键字,这个导致接口中存在方法体了,那么就)不可以有多个需要实现的方法!! 原因:根据上面lamdba的规则就可以知道,能够用lamdba表达式,那个这个接口一定是只有一个实现方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gwrmiRq2-1647686788528)(img/image-20220319090719395.png)]
比如上面这种情况,只要有俩个方法,必定报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x80lNwv7-1647686788529)(img/image-20220319090742802.png)]
上面这种情况就没有问题
- 无返回值、无参数的接口
- 无返回值、一个参数的接口
- 无返回值、多个参数的接口
- 有返回值、无参数的接口
- 有返回值、有一个参数的接口
- 有返回值、有多个参数的接口
- 返回值是对象的多参数接口
- 返回值是集合的多参数接口
2、语法精简:
- 为什么可以精简
- 怎么精简
- 精简的好处有事什么?
为什么可以精简:
- 比如我们在写普通的lambda表达式的时候,人家有返回值,那咱们方法体里面也一定要有返回值,那么问题来了,你想过没有,为啥你一定要有返回值呢?你写返回值的依据是什么呢?我们写这个方法,最后的返回值的依据就是根据接口
- 接口就是一套规范,尽管他没有方法体,也不实现具体的业务逻辑,但是他规范了我们的参数类型、规范了我们的返回值类型、规范了我们的方法名,所以我们的返回值也就是根据接口来确定的!
- 上面就是我们可以精简的原因,因为接口已经规范过了,所以有些参数类型,在lambda中我们不写也是可以的,因为接口已经规范过了,我们已经知道应该是什么类型了
同理,其他能够精简的地方,也一定是基于这个原理,接口已经规范过了, 你不说,我也明白,所以你可以不写
怎么精简?
- 参数类型可以省略(备注:``如果要省略参数类型,每一个参数类型都要省略`)
- 参数中的小括号:如果参数中只有一个参数,那么这个小括号也是可以省略的!
- 方法大括号:
if(true)
System.out.println("如果if下只有一个表达式,那么if 的大括号是可以省略的")
同理,在lambda中也是适用的,如果你只有方法体中只有一条输出语句,那么大括号也是可以省略的!!!
如果方法体中只有一个返回值,那么,返回值的return关键字是可以去掉的!!lamdbaInterface6 interface6 = (a, b) -> "城市与";
System.out.println(interface6.t6(12, "城市与dsaf"));
lamdbaInterface7 interface2 = (a , b)-> new User(10);
System.out.println(interface2.t7(1, "jaj"));
精简的好处
- 代码更加精简了
3、方法(普通方法和构造方法)引用
语法:
方法的隶属者::方法名
- 静态方法,方法隶属者就是他的类
- 普通方法,方法隶属者就是他的对象
- 构造方法:方法隶属者就是他的类
注意点:
- 参数数量和类型一定要和接口中定义的方法一致
- 返回值的类型一定要和接口中定义的方法一直
4、小知识
这个知识点有点忘记了
12 除 10 , 得到1 余 2
如果用
/
符号,则得到1 取整如果用
%
符号,则得到2 取余
System.out.println(12 / 10); // 1
System.out.println(1 / 10); // 0
System.out.println(30 / 10); // 3
System.out.println(12 % 10); // 2
System.out.println(20 % 10); // 0
三、综合案例一(集合排序)
集合排序,使用Comprator接口
- 使用内部类
- 使用lambda
比较list集合中对象的大小关系,核心代码如下:
/*下面这三种都是可以的*/
list.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() - o2.getAge();
}
});
list.sort((o1,o2)->{
return o1.getAge() - o2.getAge();
});
list.sort(Comparator.comparingInt(User::getAge));
全部代码如下:
package com.chengshiyu.app;
import com.chengshiyu.pojo.User;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Comparator;
/**
* @author 程世玉
* @create 2022/3/19 14:54
* @PROJECT_NAME JavaSE
* @Description
*/
public class main4 {
/*集合排序,对于对象进行排序*/
@Test
public void t1() {
ArrayList<User> list = new ArrayList<>();
User user1 = new User(18, "程世玉");
User user2 = new User(1, "w");
User user3 = new User(8, "j");
User user4 = new User(128, "c");
User user5 = new User(18, "s");
User user6 = new User(188, "y");
User user7 = new User(10, "l");
User user8 = new User(118, "o");
User user9 = new User(13, "v");
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
list.add(user5);
list.add(user6);
list.add(user7);
list.add(user8);
list.add(user9);
/*下面这三种都是可以的*/
list.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() - o2.getAge();
}
});
list.sort((o1,o2)->{
return o1.getAge() - o2.getAge();
});
list.sort(Comparator.comparingInt(User::getAge));
for (User user : list) {
System.out.println(user);
}
}
}
四、综合案例二(TreeSet进行排序)
比较对象,一定要重写Compartaor接口,因为set集合是没有办法直接比较两个对象的大小的
代码如下:
从大到小排,那就遇见大于,返回-1即可
从小到大排,那就遇见小于,返回-1即可
public static void main(String[] args) {
TreeSet<User> set = new TreeSet<User>((o1, o2)->{
if (o1.getAge() >= o2.getAge()){
/*前面的比后面的大,我们不想让大的排后面,就返回-1 false即可*/
return -1;
}else {
return 1;
}
});
User user1 = new User(18, "程世玉");
User user2 = new User(1, "w");
User user3 = new User(8, "j");
User user4 = new User(128, "c");
User user5 = new User(18, "s");
User user6 = new User(188, "y");
User user7 = new User(10, "l");
User user8 = new User(118, "o");
User user9 = new User(13, "v");
set.add(user1);
set.add(user2);
set.add(user3);
set.add(user4);
set.add(user5);
set.add(user6);
set.add(user7);
set.add(user8);
set.add(user9);
System.out.println(set);
}
五、综合案例五(forEach遍历)
- 全部输出怎么输出
- 增强for
- list.forEach(System.out::println);
- 选择性输出怎么输出 (如下)
核心代码:
list.forEach(e -> { if (e.getAge() > 18){ System.out.println(e); } });
全部代码
package com.chengshiyu.app;
import com.chengshiyu.pojo.User;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.TreeSet;
/**
* @author 程世玉
* @create 2022/3/19 16:48
* @PROJECT_NAME JavaSE
* @Description
*/
public class main6 {
public static void main(String[] args) {
ArrayList<User> list = new ArrayList<>();
User user1 = new User(18, "程世玉");
User user2 = new User(1, "w");
User user3 = new User(8, "j");
User user4 = new User(128, "c");
User user5 = new User(18, "s");
User user6 = new User(188, "y");
User user7 = new User(10, "l");
User user8 = new User(118, "o");
User user9 = new User(13, "v");
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
list.add(user5);
list.add(user6);
list.add(user7);
list.add(user8);
list.add(user9);
for (User user : list) {
System.out.println(user);
}
System.out.println("=================================");
list.forEach(System.out::println);
System.out.println("=================================");
/*筛选出年龄大于18的*/
list.forEach(e -> {
if (e.getAge() > 18){
System.out.println(e);
}
});
}
}
六、综合案例六(removeif实现)
核心代码:
/*1、使用迭代器方式*/ Iterator<User> iterator = list.iterator(); while (iterator.hasNext()){ User next = iterator.next(); if (next.getAge() > 18){ /*只要年龄大于18,就去除*/ iterator.remove(); } } /*2、使用lambda表达式*/ list.removeIf(e -> e.getAge() > 10);
全部代码
public static void main(String[] args) {
ArrayList<User> list = new ArrayList<>();
User user1 = new User(18, "程世玉");
User user2 = new User(1, "w");
User user3 = new User(8, "j");
User user4 = new User(128, "c");
User user5 = new User(18, "s");
User user6 = new User(188, "y");
User user7 = new User(10, "l");
User user8 = new User(118, "o");
User user9 = new User(13, "v");
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
list.add(user5);
list.add(user6);
list.add(user7);
list.add(user8);
list.add(user9);
/*1、使用迭代器方式*/
Iterator<User> iterator = list.iterator();
while (iterator.hasNext()){
User next = iterator.next();
if (next.getAge() > 18){
/*只要年龄大于18,就去除*/
iterator.remove();
}
}
/*2、使用lambda表达式*/
list.removeIf(e -> e.getAge() > 10);
七、综合案例五(线程实例化实现)
public static void main(String[] args) {
Thread thread = new Thread(()->{
for (int i = 0; i < 100; i++){
System.out.print(i);
}
});
thread.start();
}
八、Java提供的内置接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dE3Elc32-1647686788530)(img/image-20220319123329054.png)]
其实仔细观察,这个内置的接口,在我们前面写的方法中已经用到了
看源码:
1、list.removeIf方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-exp6JaIc-1647686788531)(img/image-20220319173921652.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T0znmk3e-1647686788532)(img/image-20220319173926690.png)]
2、foreach方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vItiIrxO-1647686788533)(img/image-20220319174002816.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UZreDJRr-1647686788534)(img/image-20220319174008261.png)]
3、关于TreeSet中实现的接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DdDBIzb6-1647686788535)(img/image-20220319174043516.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pbc8s47X-1647686788536)(img/image-20220319174048875.png)]
九、闭包问题
案例一:
public static void main(String[] args) {
Supplier<Integer> number = getNumber();
System.out.println(number.get());
}
public static Supplier<Integer> getNumber(){
int a = 10;
int b = 11;
return ()-> a + b;
}
案例二
public static void main(String[] args) { int a = 10; // Consumer<Integer> c = System.out::println; /*闭包,这个时候可以引用外面的参数*/ Consumer<Integer> c = (e)->{ /*闭包,这个时候可以引用外面的参数*/ /*本来是可以用上面那中方式的,但是通过这种方式能够更加清晰看到结果,假如我们传入的a,那到底是a传进去的还是他这里面本来就可以直接引入外界的a,通过这种方式能够很清楚的看到*/ System.out.println(a); }; c.accept(a); }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5hlKnhZw-1647686788537)(img/image-20220319184522520.png)]
十、Comparator 和 Comparable 接口
1、Comparable
Comparable是排序接口。若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。
作用:
此接口只有一个方法compare,比较此对象与指定对象的顺序,如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
实例:我们有两个User对象,我们如何来比较二者的大小呢?我们可以通过User实现Comparable接口来实现
代码如下:
实体类实现Comparable接口,重写CompareTo方法,定义的比较对象规则就是比较两个对象的年龄,哪个年龄大哪个对象就大
返回值规则:
- 小于则返回负数
- 等于则返回0
- 大于则返回正数
@Data
@AllArgsConstructor
@NoArgsConstructor
public class t1 implements Comparable<t1>{
private Integer age;
@Override
public int compareTo(t1 o) {
/*小于则返回负数,等于则返回0 大于则返回正数*/
return this.age - o.age;
}
}
测试
public class Demo01 {
@Test
public void test(){
t1 a = new t1(18);
t1 b = new t1(17);
/*比较量对象的年龄*/
int i = a.compareTo(b);
System.out.println(i);
}
}
2、Comparator
Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过实现Comparator来新建一个比较器,然后通过这个比较器对类进行排序
Comparator源码如下:
package java.util;
public interface Comparator<T>
{
int compare(T o1, T o2);
boolean equals(Object obj);
}
案例一:使用匿名内部类方式
@Test
public void t1() {
ArrayList<User> list = new ArrayList<>();
User user1 = new User(18, "程世玉");
User user2 = new User(1, "w");
User user3 = new User(8, "j");
User user4 = new User(128, "c");
User user5 = new User(18, "s");
User user6 = new User(188, "y");
User user7 = new User(10, "l");
User user8 = new User(118, "o");
User user9 = new User(13, "v");
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
list.add(user5);
list.add(user6);
list.add(user7);
list.add(user8);
list.add(user9);
/*下面这三种都是可以的*/
list.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() - o2.getAge();
}
});
案例二、采用lambda表达式
public void t1() {
ArrayList<User> list = new ArrayList<>();
User user1 = new User(18, "程世玉");
User user2 = new User(1, "w");
User user3 = new User(8, "j");
User user4 = new User(128, "c");
User user5 = new User(18, "s");
User user6 = new User(188, "y");
User user7 = new User(10, "l");
User user8 = new User(118, "o");
User user9 = new User(13, "v");
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
list.add(user5);
list.add(user6);
list.add(user7);
list.add(user8);
list.add(user9);
/*下面这三种都是可以的*/
list.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() - o2.getAge();
}
});
// list.sort(Comparator.comparingInt(User::getAge));
list.sort((o1,o2)->{
return o1.getAge() - o2.getAge();
});
for (User user : list) {
System.out.println(user);
}
}
3、Comparable和Comparator区别比较
Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。而Comparator是比较器,我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
说人话,最简单的理解就是
- Comparable接口必须实现ComparateTo方法,根据这个方法,就可以判断出来,是这个跟对象跟谁比较,那也就是说这个对象自己有排序的规则,我自己就有规则能够判断我这个对象比你大还是比你小,
所以这个接口一般是应用在两个对象自己进行比较
- Comparator接口里面有Comparator方法,根据源码,根据人家这个方法,就很容易判断理解出来,这个接口其实是对两个对象的比较,也就是说他相当于一个裁判,判决两个对象哪个大,哪个小,
- Comparator应用一般是在list集合中等集合中,为啥呢,
list集合中可以放各种类型的对象
,如果要对list集合中的对象进行排序
,list再要求每个对象都要有自己的排序规则不现实
,所以Comparator
应运而生,这时候list就是裁判了,他有了判决的标准了,所以可以看一下list中的sort方法,就是实现的这个Comparator接口!!!!
问题:
问题一
public String t6(int age, String name) {
return age +"左边 int 右边 String 两个相加极为String" + name;
}
返回值是String 类型,为啥一个int类型 + String类型 就可以转换为String类型,为啥?什么原理?
问题二
闭包,为什么就可以引用外界的局部变量呢?
匿名内部类是不是也是可以的?