非原创,知识总结性文章
1、Lambda表达式的介绍
函数式编程思想概述
在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。
传统写法代码示例
public class demo01 {
public static void main(String[] args) {
Runnable runnable = new Runnable(){
public void run(){
System.out.println("多线程任务执行");
}
};
new Thread(runnable).start();
}
}
说明:
- Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;
- 为了指定run的方法体,不得不需要Runnable接口的实现类;
- 为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;
- 必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
- 而实际上,似乎只有方法体才是关键所在
上述示例使用Lambda的写法
public class demo01 {
public static void main(String[] args) {
new Thread(()->{
System.out.println("多线程启动");
}).start();
}
}
上例中,核心代码其实只是如下所示的内容:() -> System.out.println("多线程任务执行!")
Lambda标准格式
Lambda省去面向对象的条条框框,格式由3个部分组成:
- 一些参数;
- 一个箭头;
- 一段代码。
Lambda表达式的标准格式为:(参数类型 参数名称) -> { 代码语句 }
格式说明: - 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔;
- ->是新引入的语法格式,代表指向动作;
- 大括号内的语法与传统方法体要求基本一致。
练习:使用Lambda标准格式(无参无返回)
给定一个厨子Cooker接口,内含唯一的抽象方法makeFood,且无参数、无返回值。如下:
public interface Cooker {
void makeFood();
}
在下面的代码中,请使用Lambda的标准格式调用invokeCook方法,打印输出“吃饭啦!”字样:
public class demo01 {
public static void main(String[] args) {
invokeCook(() -> {
System.out.println("吃饭啦!");
});
}
private static void invokeCook(Cooker c){
c.makeFood();
}
}
Lambda的参数和返回值
需求:使用Lambda表达式对存在数组中的Person类型的对象数据按照年龄的降序输出。
传统写法:
public class Demo02 {
public static void main(String[] args) {
// 本来年龄乱序的对象数组
Person[] array = {
new Person("古力娜扎", 19),
new Person("迪丽热巴", 18),
new Person("马尔扎哈", 20) };
// 匿名内部类
Comparator<Person> comp = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o2.getAge() - o1.getAge();
}
};
Arrays.sort(array, comp); // 第二个参数为排序规则,即Comparator接口实例
for (Person person : array) {
System.out.println(person);
}
}
}
Lambda写法:
public class demo02 {
public static void main(String[] args) {
// 本来年龄乱序的对象数组
Person[] array = {
new Person("古力娜扎", 19),
new Person("迪丽热巴", 18),
new Person("马尔扎哈", 20) };
Arrays.sort(array,(Person p1,Person p2) -> {
return p2.getAge() - p1.getAge();
});
for (Person person : array) {
System.out.println(person);
}
}
}
Lambda省略格式
Lambda强调的是“做什么”而不是“怎么做”,所以凡是可以根据上下文推导得知的信息,都可以省略。例如上例还可以使用Lambda的省略写法:
public static void main(String[] args) {
invokeCalc(120, 130, (a, b) -> a + b);
}
省略规则:
在Lambda标准格式的基础上,使用省略写法的规则为:
- 小括号内参数的类型可以省略;
- 如果小括号内有且仅有一个参数,则小括号可以省略;
- 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
注意:关于第三点如果省略一个,那么必须同时都得省略。
2、Lambda表达式的应用
Lambda表达式的使用条件是
- 使用lambda表达式的类型必须是一个只含有一个抽象方法的借口
- lambda只能使用final修饰的变量
forEach遍历Map
//1.forEach遍历Map
System.out.println("\n1.forEach遍历Map");
Map<String, Object> map = new HashMap<>();
map.put("key1", 11);
map.put("key2", 12);
map.put("key3", 13);
map.put("key4", 14);
map.forEach((key,value)->{
System.out.println("key:"+key+",value:"+value);
});
输出:
1.forEach遍历Map
key:key1,value:11
key:key2,value:12
key:key3,value:13
key:key4,value:14
forEach遍历List
//2.forEach遍历List
System.out.println("\n2.forEach遍历List");
List<Integer> list = new ArrayList<Integer>() ;
list.add(1);
list.add(2);
list.add(3);
list.forEach(value->System.out.println(value));
输出:
2.forEach遍历List
1
2
3
在多线程中使用Lambda表达式
//3.在多线程中使用Lambda表达式
System.out.println("\n3.在多线程中使用Lambda表达式");
new Thread(()->System.out.println("new Thread start")).start();
输出:
3.在多线程中使用Lambda表达式
new Thread start
自定义Lambda表达式功能
//4.自定义Lambda表达式功能
interface MathOperator{
int operator(int a, int b);
}
public static void calc(int a ,int b, MathOperator operator){
System.out.println(operator.operator(a, b));
}
System.out.println("\n4.自定义Lambda表达式功能");
MathOperator add = (a,b)->a+b ;
MathOperator sub = (a,b)->a-b ;
MathOperator mul = (a,b)->a*b ;
MathOperator div = (a,b)->a/b ;
calc(3, 5, add);
calc(3, 5, sub);
calc(3, 5, mul);
calc(3, 5, div);
输出:
4.自定义Lambda表达式功能
8
-2
15
0
Predicate接口实现数据过滤
System.out.println("\n5.Predicate接口实现数据过滤");
List<String> filterList = new LinkedList<>() ;
filterList.add("1");
filterList.add("abc");
filterList.add("java");
filterList.add("python2");
filterList.add("python3");
filterList.add("c++");
filture(filterList, str->str.startsWith("j")) ;
//单条件过滤
filterList.stream().filter(str->str.contains("++")).forEach(value->System.out.println(value)) ;
//多条件逻辑过滤
Predicate<String> startWith = str->str.startsWith("p") ;
filterList.stream().filter(startWith.and(str->str.contains("2"))).forEach(value->System.out.println(value)) ;
输出:
5.Predicate接口实现数据过滤
java
c++
python2
Lambda的map和reduce操作
/3.在多线程中使用Lambda表达式
System.out.println("\n6.Lambda的map和reduce操作");
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
//map操作让每一个元素加上它之前的0.12倍
costBeforeTax.stream().map(cost -> cost+0.12*cost).forEach(System.out::println);
System.out.println(costBeforeTax.stream().reduce((sum,cost)->sum+cost).get()); //求和
输出:
6.Lambda的map和reduce操作
112.0
224.0
336.0
448.0
560.0
1500
创建一个字符串列表,每个字符串长度大于2
System.out.println("\n7.创建一个字符串列表,每个字符串长度大于2");
List<String> strList = Arrays.asList(new String[]{"abc","de","abcde"});
List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);
输出:
7.创建一个字符串列表,每个字符串长度大于2
Original List : [abc, de, abcde], filtered list : [abc, abcde]
获取数字的个数、最小值、最大值、总和以及平均值
System.out.println("\n8.获取数字的个数、最小值、最大值、总和以及平均值");
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());
输出:
8.获取数字的个数、最小值、最大值、总和以及平均值
Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9
网友评论