概述
百度百科的定义:
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
- JDK8开始支持Lambda表达式,用来让程序编写更优雅
- 利用Lambda可以更简洁的实现匿名内部类与函数声明与调用
- 基于Lambda提供stream流式处理极大简化对集合的操作
1.Lambda表达式 的 语法
示例:
/**
* 四则运算接口
*/
//注解:通知编译器这是函数式接口,进行 抽象方法的检查(只能有一个抽象方法)
@FunctionalInterface
public interface MathOperation {
public Float operate(Integer a,Integer b);
//用lambda表达式只能实现函数式接口(接口有且只有一个方法)
}
三种使用Lambda表达式实现函数式接口的方法
public class LambdaSample{
public static void main(String[] args) {
//1.标准使用方式
MathOperation addition = (Integer a,Integer b)->{
System.out.print("a+b=");
return a+b+0f;
};
System.out.println(addition.operate(2,3));
//2.允许忽略参数类型
MathOperation sub = (a,b)->{
System.out.print("a-b=");
return a-b+0f;
};
System.out.println(sub.operate(2,3));
//3.单行实现代码可以省略大括号和return
MathOperation multiplication = (a,b)->a*b+0f;
System.out.println("a*b="+multiplication.operate(2,3));
}
}
2.函数式编程
什么是函数式编程
- 函数式编程是基于函数式接口并使用lambda表达的编程方式
- 函数式编程理念是将代码作为可重用数据代入到程序运行中
- 函数式编程强调"你想做什么",而不是"你想怎么做"
什么是函数式接口
- 函数式接口是有且只有一个抽象方法的接口
- Java中拥有大量函数式接口,如java.lang.Runnable
- JDK8后提供了一系列新的函数式接口,位于java.util.function
函数式接口 Predicate 示例:
public class PredicateSample {
public static void main(String[] args) {
Predicate<Integer> predicate = n->n>4; //接受的参数是n ;return的是 n>4
boolean result = predicate.test(4);
System.out.println(result); //false
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
filter(list,n->n%2==1); //选出奇数
System.out.println("");
filter(list,n->n%2==0);//选出所有偶数
System.out.println("");
filter(list,n->n>5&&n%2==1);//选出 大于5 的奇数
//代码作为可重用的资源放入代码中
//依托函数式接口,将程序的代码作为判断的条件,运行时动态的带入程序,更加灵活
}
public static void filter(List<Integer> list,Predicate<Integer> predicate){
//过滤器,第二个参数接收一个 lambda表达式(自己将结果输出,没有保存)
for(Integer num:list){
if(predicate.test(num)){
System.out.print(num+" ");
}
}
};
}
依托函数式接口,将程序的代码作为判断的条件,运行时动态的带入程序,更加灵活
比如上面这个例子中的filter函数接受的参数 Predicate predicate
他是一个函数式接口,我们传入了一个 lambda表达式 n->n%2==1
函数式接口接受了我们的 lambda表达式,在调用自己的test()方法就能按我们的要求进行筛选了。
3.函数式编程与面向对象编程的比较
4.常见函数式接口的使用
Consumer
该接口只有一个 accept() 方法
适用于只需要接受一个参数且不需要任何返回的场景
/**
* Consumer 接口的使用
* 适用于只需要接受一个参数且不需要任何返回的场景
* 只有一个方法accept
*/
public class ConsumerSample {
public static void main(String[] args) {
output(s-> System .out.println("控制台打印:"+s));
output(s->{
System.out.println("向present网站发送数据包"+s);
});
}
public static void output(Consumer<String> consumer) {
String text = "一鼓作气,再而衰,三而竭。";
consumer.accept(text);
}
}
/**
控制台打印:一鼓作气,再而衰,三而竭。
向present网站发送数据包一鼓作气,再而衰,三而竭。
*/
这个例子中,output() 方法接受一个Consumer类型的函数式接口,
我们传入一个 lambda表达式的同时对这个函数式接口进行了实现,
output() 函数又调用了 Consumer接口的 accept() 方法。
Function
Function函数式接口适用于有一个输入参数且需要返回数据的功能代码
只有一个 apply() 方法。
/**
* 使用Function函数式接口生成随机定长字符串
*/
public class FunctionSample {
public static void main(String[] args) {
//第一个参数是接受的参数类型,第二个参数是返回的参数类型
Function<Integer,String> randomStringFunction = l->{
String chars = "qwertyuiopasdfghjklzxcvbnm1234567890";
StringBuffer stringBuffer = new StringBuffer();
Random random = new Random();
for(int i=0;i<l;i++){
int positon = random.nextInt(chars.length());
stringBuffer.append(chars.charAt(positon));
}
return stringBuffer.toString();
};
String result = randomStringFunction.apply(16);//生成16位的一个字符串
System.out.println(result);
}
}
首先,通过这个例子我们可以学到 生成指定长度的随机字符串的方法。
在这个例子中,我们先声明了一个 Function接口类型的变量(需要用泛型指明接受的参数类型和返回的参数类型)randomStringFunction ,然后使用 lambda表达式 对这个函数式接口进行了实现,
最后我们调用 randomStringFunction 的 apply() 方法 传入参数 l (想要的长度)就能得到返回值 stringBuffer.toString() (定长字符串)
Predicate
用于条件判断,接受一个参数,固定返回 bool 值
只有一个方法 test()
例子见前面那一个
5.Stream流式处理
- Stream流式处理是建立在Lambda基础上的多数据处理技术
- Stream对集合数据处理进行高度抽象,极大简化代码量
- Stream可对集合进行迭代,去重,筛选,排序,聚合等一系列处理
Stream流的五种创建方式
/**
* Stream 流对象 的五种创建方式
* 常用的 是基于数组和集合
* 后三种方法简单了解
*/
public class StreamGenerator {
//基于数组创建
@Test
public void generator(){
String[] arr = {"Lily","Lucy","Zhang","Mike"};
Stream<String> stream = Stream.of(arr);//创建流对象(下面再使用流对象的方法简化开发)
stream.forEach(s-> System.out.println(s)); //forEach 中使用lambda表达式
}
//基于集合创建
@Test
public void generator2(){
List<String> list =new ArrayList<>();
list.add("LiLy");
list.add("Mike");
Stream<String> stream = list.stream();
stream.forEach(s-> System.out.println(s));
}
//基于generator方法创建无限长度数据流
@Test
public void generator3(){ //为甚么产生了无限长度的数据流
Stream<Integer> stream = Stream.generate(()->new Random().nextInt(100000)); //长生的数据不超过100000
stream.limit(10)
.forEach(i-> System.out.println(i));
}
//4.基于迭代器创建流
@Test
public void generator4(){
Stream<Integer> stream = Stream.iterate(2,n->n+2); //从1开始 步长为1
stream.limit(10).forEach(i-> System.out.println(i)); //limit限制产生的个数
}
//5.基于字符序列创建流
@Test
public void generator5(){
String str = "Aa_good_boy_中文";
IntStream stream = str.chars(); //存着每个字符的 ascall 码(汉字显示Unicode编码)
stream.forEach(c-> System.out.println((char) c));
}
}
Stream的常用方法
常用方法示例:
public class StreamMethod {
//1.提取集合中所有偶数并求和
@Test
public void case1(){
List<String> list = Arrays.asList("1","2","3","4","5");
int sum = list.stream() //获取stream对象
.mapToInt(s->Integer.parseInt(s)) //将流中的每一个元素转换为整数
.filter(n->n%2==0) //对流中的数据进行过滤,满足条件的保留
.sum(); //求和
System.out.println(sum);
}
//2.所有名字首字母大写
@Test
public void case2(){
List<String> list = Arrays.asList("lili","lucy","mike");
List<String> nameList = list.stream()
.map(s->s.substring(0,1).toUpperCase()+s.substring(1))
//.forEach(s-> System.out.println(s)); //直接遍历流
.collect(Collectors.toList()); //将流中的数据转换为一个新的List集合
System.out.println(nameList);
}
//3.将所有奇数从大到小进行排序,切不出现重复
@Test
public void case3(){
List<Integer> list = Arrays.asList(1,1,3,6,6,4,3,9,5,5);
List resultList = list.stream()
.distinct() //去重
.filter(n->n%2==1)
.sorted((a,b)->b-a) //从大到小
.collect(Collectors.toList()); //将流中的数据转换为一个新的List集合
System.out.println(resultList);
}
}
注意这一句:.collect(Collectors.toList()); //将流中的数据转换为一个新的List集合
这样我们对原来的对象进行处理后就可以得到一个新的对象,然后就可以声明一个变量来接收这个新的对象。