Java8中一个非常重要的特性就是Lambda表达式,我们可以把它看成是一种闭包,它允许把函数当做参数来使用,是面向函数式编程的思想,一定程度上可以使代码看起来更加简洁。
其实以上都不重要,重要的是能够提高我的开发效率,为了工作效率避免无意义加班,使用Lambda表达式只能说好爽!一时用一时爽,一直用一直爽!但是学习可能很多人觉得很复杂于是放弃了,可读性确实很不好,我的建议是死记规则,学习常用的Lambda表达式就够了。因为,其他稀奇古怪的语法很可能被你老大看见了会请你喝茶,所以本篇博客就由浅入深介绍提高工作效率神器——Lambda常用表达式。
比如创建一个线程并启动,一般我们这样写:
//匿名内部类写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("内部类写法");
}
}).start();
使用Lambda表达式可以这样写:
new Thread(() -> System.out.println("hello world")).start();
我知道你很急,但你先别急,后面我们详细介绍其用法。以上代码表明使用Lambda表达式写起来真的很爽,早写完早下班!如果你司是代码量绩效那别学了,先辞职再说![手动狗头]
Lambda表达式的最最最常见的使用场景是 接口和容器,凡是以后需要实现接口和操作容器都可以考虑使用Lambda表达式。实现接口需要注意,如果接口中仅有一个抽象方法需要实现可以使用Lambda表达式,否则有多个未实现的方法不能使用。上述例子也就是接口使用Lambda表达式的一种实现方式。下面分为接口和容器结合例子进行讲解。
首先定义我们自己的接口,就是打印输入的字符串s,如下:
interface MyPrint{
public void print(String s);
}
传统的实现方式就是需要实现MyPrint类然后重写print方法,这里就不再展示,我们直接使用Lambda表达式实现如下:
MyPrint myPrint = s -> System.out.println(s);
myPrint.print("hello world 1");
现在解释上述代码:等号左边是我们的接口类,使用多态的方式父类引用指向子类实现。那么右边就是我们对左边父类的实现。() -> {} 是Lambda的一个语法,()中的字符表示需要传入的参数,不需要写类型,使用占位符即可,如果()中没有参数表示该方法(指的是需要重写的方法)无参。{} 是方法体,具体的实现代码写在 {} 中。如果有多个参数可以写成 (a,b)->{return a+b} ,但是这还不够简介,记住:如果(a)只有一个参数那么不需要写 () 直接写 a,如果 {} 只有一行代码如果有return则不用写 return 也不用写 {},就写出一行需要返回的逻辑实现即可,例如上面代码实现标准写法是:
MyPrint myPrint = (s) -> {System.out.println(s)};
这里,我们再举几个例子说明如下:
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
总结就是:能偷懒就偷懒,()->{} ,出错那你就按照标准的语法写,反正编译器会告诉你,如果只有一个参数可以省略() ,如果方法体只有一行代码可以省去return 单词和{} 。是不是超级简单,那么下面要升级难度了,
上述写法还可以进一步写成如下:
MyPrint myPrint = System.out::println; //等价于 s -> System.out.println(s);
myPrint.print("hello world 2");
这里就不得不介绍 :: 号的用法了,我们放到下一节讲解!
对于 :: 我的理解如下,假设有个Student类,其中有个方法 getName() ,那么student::getName 的意思就是 传入Student的对象student,调用它的getName()方法。等价于 (Student s)->{s.getName()} , 即
student::getName 等价于 (s)->{s.getName()} 或者 (Student s)->{s.getName()}
所以,System.out::println 双冒号左边是对象,右边是该对象的方法。那有人会问那么参数s如何传的呢?我们再看,System.out::println; 等价于 s -> System.out.println(s); 其实println需要的是一个字符串参数,而MyPrint的print接口也是一个字符串参数,这种一对一的参数传递其实是隐式传递的,而且如果使用这种写法,参数必须是对应的!
以上情况是 对象::实例方法名; 那么也可以有 类名::静态方法名,例如 Integer::parseInt ,就是接受一个字符串,解析为整数。等价于 (String s)->{ Integer.parseInt(s)}。你会问你怎么知道参数是String,因为parseInt方法需要的就是String 啊。因此,如果你明白啦以上的规则,其实非常简单!现在还不适应没关系,通过下面的容器集合流处理案例,你会很明白!
这里就非常简单了,基本都是Stream流的处理,配合Lambda表达式,简直写起来不要太爽了!这里还是以语法结合案例为驱动来讲解:
使用它替代 for(int a : arr) ,简直太爽了,如下:
// 使用Lambda表达式替代for循环
List<Integer> arr = Arrays.asList(4, 1, 25);
// forEach 表示从容器中依次取出每个元素进行{}中的操作
arr.forEach(num->{
int tmp = num+1;
System.out.println(tmp);
});
对容器中每个对象都进行过滤,如果filter( lambda条件)中的条件判断为true则保留,条件为false则丢弃。案例:提取容器中的奇数数字。
List<Integer> arr = Arrays.asList(4, 1, 25,3,6);
List<Integer> o = arr.stream().filter(num -> num % 2 == 1).collect(Collectors.toList());
System.out.println(o);
.collect(Collectors.toList() 表示重新转为List容器。
对容器中的每个对象都遍历,并对它在 map( lambda操作 ) 中进行一个Lambda表达式的操作,例子:每个数字变成自身的2倍(使用forEach也可以实现):
List<Integer> arr = Arrays.asList(4, 1, 25,3,6);
List<Integer> d = arr.stream().map(num -> 2 * num).collect(Collectors.toList());
System.out.println(d);
例子:将下面数组按照其字符串长度分组。
List<String> phones = Arrays.asList("huawei", "xiaomi", "vivo", "iphone","oppo", "laoluo", "oneplus");
Map<Integer, List<String>> map = phones.stream().collect(Collectors.groupingBy(String::length));
System.out.println(map);
最后来一道综合练习题,请先自己做,再看答案!
将一个字符串数组其中的字符串对应的奇数取出来,再转为整数类型,再排序
List list = Arrays.asList(“23”, “4”, “11”, “7”, “3”, “8”, “10”);
应该得到 [3, 7, 11, 23] .
List<Integer> res = list.stream().map(Integer::parseInt).filter(a -> a % 2 == 1).sorted().collect(Collectors.toList());
System.out.println(res);
以上就是 提高工作效率,但不能提高代码效率的常用Lambda表达式。对你有帮助的话帮忙一键三连,点赞-评论-关注!
假设有如下Student类,我们有关于这个类的集合,那么我们可以有以下操作!
package strategy.demo.entity;
public class Student {
private String name;
private int age;
private int score;
private String address;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getScore() {
return score;
}
public String getAddress() {
return address;
}
public void setScore(int score) {
this.score = score;
}
public Student(String name, int age, int score, String address){
this.name = name;
this.age = age;
this.score = score;
this.address = address;
}
public String toString(){
return "Student [name=" + name + ", age=" + age + ", score=" + score + ", address=" + address + "]";
}
}
下面就是对上面的练习:
package strategy.demo;
import org.junit.Test;
import strategy.demo.entity.Student;
import strategy.demo.util.StudentHelps;
import java.util.List;
import java.util.Map;
import java.util.OptionalDouble;
import java.util.stream.Collectors;
public class streamDemo {
/**
* 中间操作:过滤(filter)、映射(map)、遍历(peek)、扁平化(flatMap)、排序(sorted)、去重(distinct)、截取(limit)、跳过(skip)
* 终端操作:匹配(anyMatch、allMatch、noneMatch)、查找(findFirst、findAny)、遍历(forEach)
* 计数(count)、归约(reduce)、收集(collect)、聚合(max、min、sum、average)
* @param args
*/
List<Student> allStudents = StudentHelps.getAllStudents();
@Test
public void filterDemo(){
// 过滤年龄大于20的学生
allStudents .stream()
// filter 接受一个谓词判断函数,true表示保留,false表示丢弃
.filter(student -> student.getAge()>20)
.forEach(System.out::println);
}
@Test
public void mapDemo(){
// 映射出所有学生的姓名
allStudents .stream()
// map 接受一个转换函数,将输入的元素转换成另外的元素,这里将Student转换成String
.map(student -> student.getName()) // 等价于 .map(Student::getName)
.forEach(System.out::println);
// 将每位学生的分数上调10分
allStudents.stream()
.map(student -> {student.setScore(student.getScore()+10); return student;})
.forEach(System.out::println);
}
@Test
public void peekDemo(){
// peek 与 forEach 类似,但是不会销毁流,peek之后可以继续操作而forEach之后不能继续操作
// 与map的操作很类似,但是map是转换操作,而peek是消费操作,两者功能基本上可以互换
allStudents .stream()
.peek(student -> System.out.println(student.getName()))
.forEach(System.out::println);
}
@Test
public void sortedDemo(){
// sorted 排序,接受一个Comparator比较器,可以自定义排序规则
allStudents .stream()
.sorted((s1,s2)->s1.getScore()-s2.getScore())
.forEach(System.out::println);
}
@Test
public void distinctDemo(){
// distinct 去重,根据元素的hashCode和equals方法来判断是否重复
allStudents .stream()
.distinct()
.forEach(System.out::println);
}
@Test
public void limitDemo(){
// limit 截取,只保留前n个元素
allStudents .stream()
.sorted((s1,s2)->s2.getScore()-s1.getScore())
.limit(3)
.forEach(System.out::println);
}
@Test
public void skipDemo(){
// skip 跳过,跳过前n个元素
allStudents .stream()
.sorted((s1,s2)->s2.getScore()-s1.getScore())
.skip(3)
.forEach(System.out::println);
}
@Test
public void anyMatchDemo(){
// anyMatch 匹配,只要有一个元素满足条件就返回true
boolean b = allStudents .stream()
.anyMatch(student -> student.getScore() > 80);
System.out.println(b);
}
@Test
public void allMatchDemo(){
// allMatch 匹配,所有元素都满足条件才返回true
boolean b = allStudents .stream()
.allMatch(student -> student.getScore() > 50);
System.out.println(b);
}
@Test
public void noneMatchDemo(){
// noneMatch 匹配,所有元素都不满足条件才返回true
boolean b = allStudents .stream()
.noneMatch(student -> student.getScore() > 100);
System.out.println(b);
}
@Test
public void findFirstDemo(){
// findFirst 查找,返回第一个大于80分的学生元素
Student student = allStudents .stream()
.filter(s -> s.getScore() > 80)
.findFirst()
.get();
System.out.println(student);
}
@Test
public void findAnyDemo(){
// findAny 查找,返回任意一个大于80分的学生元素
Student student = allStudents .stream()
.filter(s -> s.getScore() > 80)
.findAny()
.get();
System.out.println(student);
}
@Test
public void countDemo(){
// count 计数,返回流中大于80分学生元素的个数
long count = allStudents .stream()
.filter(s -> s.getScore() > 80)
.count();
System.out.println(count);
}
@Test
public void collectDemo(){
// collect 收集,将流中的元素收集到一个集合中
List<Student> students = allStudents .stream()
.filter(s -> s.getScore() > 80)
.collect(Collectors.toList());
System.out.println(students);
// group 收集,将流中的元素收集到一个集合中
Map<String, List<Student>> students1 = allStudents .stream()
.filter(s -> s.getScore() > 80)
.collect(Collectors.groupingBy(Student::getAddress)); // 按照地址进行分组
System.out.println(students1);
// partition 分区,将流中的元素按照谓词逻辑分为两组
Map<Boolean, List<Student>> students2 = allStudents .stream()
.filter(s -> s.getScore() > 80)
.collect(Collectors.partitioningBy(s -> s.getScore() > 90)); // 按照分数是否大于90分进行分区
System.out.println(students2);
}
@Test
public void maxDemo(){
// max 聚合,返回流中分数最高的学生
Student student = allStudents .stream()
.max((s1, s2) -> s1.getScore() - s2.getScore())
.get();
System.out.println(student);
OptionalDouble maxScore = allStudents.stream()
// 将Student转换成double类型的分数
.mapToDouble(Student::getScore)
.max();
System.out.println(maxScore.getAsDouble());
}
@Test
public void minDemo(){
// min 聚合,返回流中分数最低的学生
Student student = allStudents .stream()
.min((s1, s2) -> s1.getScore() - s2.getScore())
.get();
System.out.println(student);
OptionalDouble minScore = allStudents.stream()
// 将Student转换成double类型的分数
.mapToDouble(Student::getScore)
.min();
System.out.println(minScore.getAsDouble());
}
@Test
public void sumDemo(){
// sum 聚合,返回流中所有学生的分数总和
int sum = allStudents .stream()
.mapToInt(Student::getScore)
.sum();
System.out.println(sum);
}
@Test
public void averageDemo(){
// average 聚合,返回流中所有学生的分数平均值
double average = allStudents .stream()
.mapToInt(Student::getScore)
.average()
.getAsDouble();
System.out.println(average);
}
}