Java8 Lambda表达式
为什么要引入Lambda表达式
简单说,引入lambda表达式就是为了简化代码,允许将函数作为一个方法的参数传递进方法中;
回顾一下Java8以前,如果想把某个接口的实现类作为参数传递给一个方法会怎么做?要么创建一个类实现该接口,然后new出一个对象,在调用方法时传递进去,要么使用匿名类,可以精简一些代码。以创建一个线程并打印一行日志为例,使用匿名函数写法如下:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("欢迎关注公众号:程序新视界");
}
}).start();
在java8以前,使用匿名函数已经算是很简洁的写法了,再来看看使用Lambda表达式,上面的代码会变成什么样子。
new Thread(() -> System.out.println("欢迎关注公众号:程序新视界")).start();
我们都知道java是面向对象的编程语言,除了部分简单数据类型,万物皆对象。因此,在Java中定义函数或方法都离不开对象,也就意味着很难直接将方法或函数像参数一样传递,而Java8中的Lambda表达式的出现解决了这个问题。
Lambda表达式使得Java拥有了函数式编程的能力,但在Java中Lambda表达式是对象,它必须依附于一类特别的对象类型——函数式接口(functional interface),后面详细讲解。
Lambda表达式简介
Lambda表达式是一种匿名函数(对Java而言这并不完全准确),通俗的说,它是没有声明的方法,即没有访问修饰符、返回值声明和名字的方法。使用Lambda表达式的好处很明显就是可以使代码变的更加简洁紧凑。
Lambda表达式的使用场景与匿名类的使用场景几乎一致,都是在某个功能(方法)只使用一次的时候。
Lambda表达式的语法结构
//没有参数
() -> body
// 1个参数
(param) -> body
// 或
(param) ->{ body; }
// 多个参数
(param1, param2...) -> { body }
// 或
(type1 param1, type2 param2...) -> { body }
代码配合Lombda表达式简洁明了,逻辑清晰(下面有Lomdba表达式语法格式)
import java.util.*;
import java.util.stream.Stream;
/**
* stream 使用方法
*/
public class stream01 {
public static void main(String[] args) {
//声明一个list
List<String> list = new ArrayList<>();
list.add("周芷若");
list.add("张无忌");
list.add("张三丰");
list.add("周周");
//正常情况过滤list元素
for (String s:list){
if (s.startsWith("周")){ //筛选姓周的
System.out.println(s);
}
}
// stream 流 核心理念 只关注实现,不关注如何实现,如fliter只是过滤,forEach只是遍历
list.stream()
.filter(aa -> aa.startsWith("周")) //过滤姓周的
.filter(aa -> aa.length() == 3) //过滤长度为3的字符
.forEach(aa -> System.out.println(aa)); //forEanch遍历
// 使用stream流有两种模式,1 集合对象获取如 list.stream() 2 stream流静态方法 对象.of
// 第一种方式示例 集合类
//List 类
List<String> lista = new ArrayList<>();
list.stream();
//Set 类
Set<String> set = new HashSet<>();
set.stream();
//Map 类
Map<String, String> map = new HashMap<>();
//因为集合才能获取stream所以获取键,存储到一个set集合里 获取stream流
Set<String> set1 = map.keySet();
set1.stream();
//获取值,存储到collection集合里,获取stream流
Collection<String> values = map.values();
values.stream();
//这种方式更加简洁,entrSet 可以获取键、值映射关系
Set<Map.Entry<String, String>> entries = map.entrySet();
entries.stream();
//第二种方式使用stream流静态方法of 可以用于数组
String[] str = {"字符串","字符串"};
Stream.of(str);
//Stream流的静态方法of可以传入可变参数,也就是说可以传入数组
Stream.of("字符串","字符串").forEach(name -> System.out.println(name));
// Stream 流 分两种方法
// 1 延迟方法:返回值类型任然是Stream自身类型的方法、因此支持链式调用(除了终结方法外、其余方法都是链式调用)
// 2 终结方法:返回值类型不在是Stream自身类型的方法、因此不支持链式调用
//Stream常用方法 forEach、filter、Map、Count、limit、skip、concat
//forEach 方法: 遍历流中的数据属于“终结方法”、遍历之后就不能再调用了
//获取一个流并遍历,使用Lambda表达式
Stream.of("熊大","熊二","熊三","熊四","熊五").forEach(name -> System.out.println(name));
//fliter 方法: 对流中元素进行过滤
//声明一个流
Stream<String> stringStream = Stream.of("熊大", "熊二", "李三", "熊四", "熊五");
//对流中元素进行过滤只要李三、返回值是一个新的流
Stream<String> stringStream1 = stringStream.filter((String name) -> {
return name.startsWith("李");
});
stringStream1.forEach(name -> System.out.println(name));
//Stream流属于管道流,只能被消费(使用)一次、第一个Stream流调用方法完毕、数据就会流转到下一个Stream流中而第一流就关闭了
//映射Map方法: 如果需要将流中的元素转换到下一个流中可以使用Map方法
//声明一个流
Stream<String> stream = Stream.of("1", "2", "3", "4", "5"); //第一个流
//使用Map方法把字符转换成整数、转换(映射)为Integer类型整数
Stream<Integer> integerStream = stream.map((String s) -> { //第二个流(延迟方法会返回一个新的流)
return Integer.parseInt(s);
});
integerStream.forEach(i -> System.out.println(i));
//统计Count方法: 用于统计流中元素个数、返回值为long类型、属于终结方法
ArrayList<Integer> integers =new ArrayList<>();
integers.add(1);
integers.add(2);
integers.add(3);
Stream<Integer> stream1 = integers.stream(); //返回一个新的流
long count = stream1.count();
System.out.println(count);
//截取limit方法:用于截取流中元素、只能截取前n个、属于延迟方法
//声明数组
String[] arr = {"喜洋洋","懒洋洋","灰太狼"};
//放入流里
Stream<String> stream2 = Stream.of(arr);
Stream<String> limit = stream2.limit(2);
limit.forEach(name -> System.out.println(name));
//跳过skip方法: 用于跳过、返回一个新的流、属于延迟方法
String[] arrr = {"喜洋洋","懒洋洋","灰太狼"};
//放入流里
Stream<String> stream3 = Stream.of(arrr);
Stream<String> skip = stream3.skip(2);
skip.forEach(name -> System.out.println(name));
//组合concat方法: 将两个流合并一个流
Stream<String> concat1 = Stream.of("熊大", "熊二", "李三", "熊四", "熊五");
Stream<String> concat2 = Stream.of("喜洋洋","懒洋洋","灰太狼");
//把以上两个流组合成一个流
Stream<String> concat = Stream.concat(concat1, concat2);
concat.forEach(name -> System.out.println(name));
}
}
一、Lambda 表达式的基础语法:Java8中引入了一个新的操作符 “->” 该操作符称为箭头操作符或 Lambda 操作符
- 箭头操作符将 Lambda 表达式拆分成两部分:
- 左侧:Lambda 表达式的参数列表
- 右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体
语法格式一:无参数,无返回值 -
() -> System.out.println("Hello Lambda!");
语法格式二:有一个参数,并且无返回值
-
(x) -> System.out.println(x)
语法格式三:若只有一个参数,小括号可以省略不写
-
x -> System.out.println(x)
语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
Comparator com = (x, y) -> {
System.out.println(“函数式接口”);
return Integer.compare(x, y);
};
语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
Comparator com = (x, y) -> Integer.compare(x, y);
语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
(Integer x, Integer y) -> Integer.compare(x, y);
上联:左右遇一括号省
下联:左侧推断类型省
横批:能省则省
二、Lambda 表达式需要“函数式接口”的支持
- 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。 可以使用注解 @FunctionalInterface 修饰
- 可以检查是否是函数式接口