Lambda简介
Lambda表达式是java8的特性(现在已经14了我就把“新”去了哈),主要依靠操作符"->"来让符合 输入( x , y ) -> {输出 x 与 y 运算后的结果} 的代码能够更加的简洁与灵活。
使用Lambda表达式需要声明一个函数式接口(有且仅有一个抽象方法的接口)。
比如,我们自己定义一个求和的函数式接口并用Lambda表达式进行运算:
//定义一个接口
public interface MyAdd {
public int add(int a,int b);
}
//定义测试类
public class AddTest01 {
public static void main(String[] args) {
//写法是很智能的,因为接口的参数是int型,所以省略数据类型是可以的
MyAdd add1 = (a,b) -> a + b;
//因为返回值类型是int,所以不加return也认
MyAdd add2 = (int a,int b) -> a + b;
//最全乎的格式
MyAdd add3 = (int a,int b) -> {return a + b;};
System.out.println(add1.add(1, 2));
System.out.println(add2.add(1, 2));
System.out.println(add3.add(1, 2));
}
}
运算结果如下
3
3
3
除了我们自己定义的接口以外,这个Lambda也可以很好的改善我们原先的一些代码。比如之前我们把Student对象存入TreeSet时,如果Student没有实现Comparable并且在new TreeSet的时候没有传入比较器的话,就会报错java.lang.ClassCastException: day01.Student cannot be cast to java.lang.Comparable所以我们会通过匿名内部类的方式传入一个比较器,代码如下:
//此处省略Student类以及new学生的代码了
...
//创建TreeSet
Set<Student> set = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2){
return s1.getAge() - s2.getAge();
}
});
这个时候我们再看看用Lambda表达式如何改写
//创建TreeSet
Set<Student> set = new TreeSet<>((stu1,stu2) -> s1.getAge() - s2.getAge());
没错,这就完了,对比一下就能发现其实就是根据年龄作比较,很简单的一个比较逻辑还要new个比较器再重写方法写这么一套是不是有些麻烦,这样一看Lambda确实有它存在的合理性了对吧?
Stream简介
Stream流操作也是java8加进来的,是基于上面的函数式编程的在集合类上进行复杂操作的工具。之前都需要通过写遍历或者写迭代器相关代码对于每个元素进行筛选,但运用Stream就可以只传入规则,剩下的交给集合类去做,我们坐等结果就好了。
我的理解
- Stream的操作分为两大类:中间操作 终止操作
- 概念区分: 最直接的就是看这个方法结束之后返回地还是不是Stream。就像是大盘chicken,先抓个chicken(获取数据源),然后进行中间操作拔毛(distinct),按大小个摆盘(sorted)等等,不管咱们在后厨拔毛还是称重或者干啥,这个chicken还是chicken,还没有端上桌你就还有曹作的余地,但是只要上桌了(collect等终止操作),你就别想再拔个毛再给人换个小点的chicken干啥了,一切都结束了,晚矣。
Stream实例记录(上代码)
中间操作
排序(sorted):先通过list.stream()获取数据源,然后调用sorted方法进行排序(需要元素实现了Comparable),最后使用终止操作collect(Collector<? super T, A, R> collector)再次转换为集合,里面参数看不懂,不过java8提供了Collectors来简化,转list就如代码所示,转Map也有其对应的方法。
//获取一个集合
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(3);
list1.add(7);
list1.add(5);
list1.add(5);
list1.add(6);
//获得排序过的list
List<Integer> newList = list1.stream().sorted().collect(Collectors.toList());
//输出
System.out.println(newList);
执行结果:
[1, 3, 5, 5, 6, 7]
后面的例子都是先取数据源,最后转换回list的时候,(stream,collect)这两个方法就不再重复解说了哈。
筛选(filter):调用filter()方法来保留符合规则的元素,需要传入Predicate对象,重写其test方法,返回true的元素留下。我们现在只要能被3整除的元素。示例集合:[1, 3, 5, 5, 6, 7]
//获得符合规则元素的list
List<Integer> newList = list1.stream().filter(new Predicate<Integer>() {
@Override
public boolean test(Integer t) {
return t%3 == 0;
}
执行结果:
[3, 6]
去重(distinct):调用distinct()方法去掉重复项,类似SQL。示例集合:[1, 3, 5, 5, 6, 7]
//获得去重过的list
List<Integer> newList = list1.stream().distinct().collect(Collectors.toList());
执行结果:
[1, 3, 7, 5, 6]
映射(map): 调用map,使所有元素根据给定的规则生成新的一个数据流。比如我要原list所有元素的值翻一倍。示例集合:[1, 3, 5, 5, 6, 7]
//获得翻倍过的list
List<Integer> newList = list1.stream().map((num1) -> num1*2).collect(Collectors.toList());
执行结果:
[2, 6, 14, 10, 12, 10]
终止操作
最终解(reduce):调用reduce ,将这个数据流根据运算返回一个最终结果,放入结果容器(Optional)中,不过这个方法有三种形式,分别演示一下,个人认为这也是终止操作里最难的嘞
一个参数:reduce(BinaryOperator accumulator) 返回最大的一个值。示例集合:[1, 3, 5, 5, 6, 7]
//获得最大值
Optional<Integer> result = list1.stream().reduce((num1,num2)->num1 > num2?num1:num2);
执行结果:
Optional[7]
两个参数:reduce(T identity, BinaryOperator accumulator) 返回最大的一个值,不过他会让第一个参数也参入运算。示例集合:[1, 3, 5, 5, 6, 7]
//获得最大值,999也参加运算
Integer result = list1.stream().reduce(999,(num1,num2)->num1 > num2?num1:num2);
执行结果:
999
参数不同发现返回值也直接成了一个Integer而不再是Optional对象
三个参数:有点高深,还牵扯到并行,正在学习,大家也可以在评论里讨论一下。
还有:
collect(收集): 上面每个例子都用到了,大家应该也能感受了,第一个例子也进行了说明。
max、min、anyMatch、allMatch 、findAny、findFirs、skip、foreach这些根据字面意思大家也应该可以猜到了,求最大,求最小,有一个满足,全部满足,找到任意一个,找第一个,跳过前几个元素,遍历等等,这些大家调一次打印一下应该都就知道是干嘛的了,只要返回的还是Stream你发现还能接着点出stream的其他操作,就是中间操作。
最后说一下,多个中间操作组合起来就是一个大的中间操作,只要没有调用终止操作中的任意一个,就一直可以继续下去。比如,先去重,再排序,再每个翻个倍,最后遍历输出。示例集合:[1, 3, 5, 5, 6, 7]
//先去重,再排序,再每个翻个倍,最后遍历输出
list1.stream().distinct().sorted().map(num1 -> num1*2).forEach(System.out::println);
如果觉得那里写的不对可以评论或者私信我,一起在激烈的讨论中擦出奇怪的火花。