JDK新特性(Lambda表达式,Stream流)
Lambda表达式:
Lambda 表达式背后的思想是函数式编程(Functional Programming)思想。在传统的面向对象编程中,程序主要由对象和对象之间的交互(方法调用)构成;而在函数式编程中,重点在于函数的应用和组合。
以上就是Lambda表示的背后思想,做为了解即可
主要要看怎么用:
Lambda表达式的格式:
()->{}
() : 重写方法的参数位置
-> : 将参数传递到方法体中
{} : 重写方法的方法体
直接看代码:
public class Test03 { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add(1); list.add(2); list.add(8); list.add(0); list.add(4); Collections.sort(list, new Comparator() { @Override public int compare(Integer o1, Integer o2) { return o1-o2; } }); System.out.println(list); Collections.sort(list,((o1, o2) -> o1-o2)); System.out.println(list); } }
上面是用Comparator比较器来定义排序规则
下面则是用Lambda表达式
我们对照起来看,就能发现(o1,o2)就是方法的参数 -> 后面就是方法体。
Lambda表达式使用前提:
在使用Lambda表达式之前,我们需要确认必须是函数式接口做为参数传递:
什么是函数式接口?
有且只有一个抽象方法的接口,用@FunctionalInterface去检测
我们可以点击这个Comparator这个方法
还有我们在线程章节经常用的Runnable接口
new Thread(()->System.out.println(Thread.currentThread().getName())).start();
这时候可能有人会想,Lambda表达式既然只能用函数式接口做为参数,那这个不是很鸡肋嘛
难道我要自己去写一个接口,里面专门再只放一个抽象方法,那这不是更麻烦了嘛
反正我刚刚学的时候,我确实是有这样的疑问
后面问了GPT,其实这个lambda表达式大部分应该是和后面的Stream流一起使用的
Stream流:
Stream流中的"流"不是特指"IO流",它是一种"流式编程"(编程方式),可以看做是"流水线"
个人对流的理解:
在我学习了流的大致用法之后,我其实觉得这个流就是一个操作数组和列表的工具类
里面提供了一系列的方法来操作数组和列表
下面介绍一下Stream流的方法:
Stream流的获取:
这个Stream流的获取就是把数组或者列表转化成流
public class Demo02Stream { public static void main(String[] args) { //1.针对集合:Collection中的方法 //Stream stream() ArrayList list = new ArrayList(); list.add("张三"); list.add("李四"); list.add("王五"); Stream stream = list.stream(); System.out.println(stream); //2.针对数组:Stream接口中的静态方法: //static Stream of(T... values) Stream stream1 = Stream.of("金莲", "三上", "松下"); System.out.println(stream1); } }
常用方法:
-
filter(Predicate):过滤方法,根据指定的条件对元素进行筛选。
-
map(Function):映射方法,将元素按照指定的映射规则进行转换。
-
forEach(Consumer):遍历方法,对流中的每个元素执行指定的操作。
-
collect(Collectors):收集方法,将流中的元素收集到一个集合中。
-
distinct():去除流中的重复元素。
-
limit(long):截取流中的前几个元素。
-
skip(long):跳过流中的前几个元素。
-
count():统计流中元素的个数。
直接上一段代码:
public class Test01 { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("张无忌"); list.add("张三丰"); list.add("张大彪"); list.add("吕不韦"); list.add("张三"); list.add("赵姬"); list.add("张翠山"); list.add("嫪毐"); //1:筛选出姓张的人 System.out.println("筛选出姓张的人"); list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.print(s+" ")); //2:筛选出姓张且名字为两个字的人 System.out.println(); System.out.println("筛选出姓张且名字为两个字的人"); list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==2).forEach(s -> System.out.print(s+" ")); //3:计算列表的个数 System.out.println(); System.out.println("计算列表的个数"); long count = list.stream().count(); System.out.println(count); //4:返回流的前n个对象 System.out.println(); System.out.println("返回流的前2个对象"); Stream limit = list.stream().limit(2); limit.forEach(s -> System.out.print(s+" ")); //5:跳过Stream流对象中的前n个元素,返回一个新的Stream流对象 System.out.println(); System.out.println("跳过Stream流对象中的前n个元素,返回一个新的Stream流对象"); list.stream().skip(2).forEach(s-> System.out.print(s+" ")); //6:两个流合成一个流 System.out.println(); System.out.println("两个流合成一个流"); ArrayList newlist = new ArrayList(Collections.nCopies(list.size(), "")); Collections.copy(newlist,list); System.out.println(newlist); Stream.concat(newlist.stream(),list.stream()).forEach(s -> System.out.print(s+" ")); //7:转换流中的类型 System.out.println(); System.out.println("转换流中的类型"); Stream stream = Stream.of(1, 2, 3, 4, 5, 6); stream.map(Integer -> Integer+"").forEach(s -> System.out.print(s+" ")); } }
下面来一个项目中的稍微复杂一点的代码加深印象:
在学这个JDK新特性这一章的时候,刚好在写伙伴匹配系统
下面这段鱼皮老师写的代码,我那个时候一直看不懂
现在算是能看懂了
这里的userList就是在数据库中查出来的所有用户列表
这里的tagNameList就是前端传过来的标签列表
所以这一段的逻辑就是根据前端传过来的标签列表进行查找有相同标签的用户
return userList.stream().filter(user -> { String tagsStr = user.getTags(); Set tempTagNameSet = gson.fromJson(tagsStr, new TypeToken() { }.getType()); tempTagNameSet = Optional.ofNullable(tempTagNameSet).orElse(new HashSet()); for (String tagName : tagNameList) { if (!tempTagNameSet.contains(tagName)) { return false; } } return true; }).map(this::getSaftyUser).collect(Collectors.toList());
我们来分析一下整体的代码逻辑:
- 将userList转成流对象
- 转成流对象之后再从对应的用户中取出标签,因为我这里将标签定义成了一个String类型所以后面就将这个String转成了JSON格式的数据存到这个Set集合中
- 用了Optional来对这个转换后的集合做了一个判空处理,如果为空,就new一个空集合
- 然后遍历前端传过来的标签列表,如果判断标签列表中有和用户的标签列表不同的元素直接返回false,这样就被流给过滤掉了
- 接着再对符合要求的用户做一个映射:map(this::getSaftyUser),这个就相当于:map(this::getSaftyUser),将当前对象(this)的 getSaftyUser 方法应用到 Stream 中的每个元素上,当我们执行完这个filter方法之后,里面的用户就是我们需要的用户了,我们需要对用户进行一个脱敏将一些重要信息隐藏起来。
- 然后再将我们粉装好的流对象转成列表返回即可
如果上面的逻辑有点难,举个例子,因为我自己一开始也想了很久
比如有三个用户ABC
他们的标签分别是
A : Java ,男,大一
B : Python ,男,大二
C : C++ ,女,大三
然后这个时候我们传入的tagNameList是:男,大一
执行流程就是:我们会先将上面的三个用户取出来成一个列表就是userList
然后将这个列表转换成流对象,接着将这个列表中的每一个对象都执行后面Lambda表达式中的操作,有点像遍历一遍这个列表,比如将A用户的标签取出来 Java ,男,大一,封装成一个Set集合,再遍历tagNameList这个列表:男,大一,挨个判断,男是否在这个集合中,在就继续判断大一这个标签是否在这个集合中,在就结束循环,返回true,就把这个A用户装到流中。