目录
1. lambda表达式作用
lambda表达式本质是语法糖,由编译器将代码转换为常规代码,是Java8的新特性,主要优点如下:
- 代码简洁,开发快速
- 接近自然语言,易于理解
- 易于“并发编程” (eg:对大数据处理时,并行流,可以多线程操作)
并行流可查看文章:函数式接口、方法引用“::”简化、并行流 && idea源码英文翻译-CSDN博客
lambd表达式快速入门
以Java中自带的list.forEach()为例,该接口的入参是Consumer类型,并重写accept实现功能,代码如下所示:
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c");
list.forEach(new Consumer<String>() {
public void accept(String s) {
System.out.println(s);
}
});
}
此时,lambda表达式可以将代码简化为一行,易于阅读和简洁。
//Lanmbda表达式
list.forEach(s -> System.out.println(s));
2. lambda表达式
2.1 lambda表达式省略规则
代码实现对arr数组遍历输出,以该代码为例
import java.util.function.IntConsumer;
public class Main {
public static void main(String[] args) {
//最开始展示格式
foreachArr(new IntConsumer() {
@Override
public void accept(int value) {
System.out.println(value);
}
});
//切换为待简化lambda格式
foreachArr((int value) -> {
System.out.println(value);
});
}
/**
* @param consumer : IntConsumer是java8提供的函数式接口之一,意思为消费者,
* 接受参数而不返回值,Consumer期望通过方法的实现来执行具体的操作。
* accept为IntConsumer下的可实现方法,接受一个参数且没有返回值。
*/
public static void foreachArr(IntConsumer consumer){
int[] arr = {1, 2, 3, 4, 5, 6};
for(int i : arr){
consumer.accept(i);
}
}
}
- 参数类型可以省略
如果参数列表可以被编译器自行推倒,可以删除参数类型
foreachArr((value) -> {System.out.println(value);});
- 方法体只有一句代码时大括号和唯一一句代码的分号可以省略
foreachArr((value) -> System.out.println(value));
- 方法只有一个参数时小括号可以省略不记
foreachArr(value -> System.out.println(value));
以上规则只供学习,如果记不住,可查看“2.2 使用idea快捷键”,转化的即为最简格式。
2.2 使用idea快捷键
在idea中,通过alt + enter可以快捷键,自动优化得到lambda表达式,如下所示:
3. stream流
3.1 stream示例
以下代码中,通过stream流进行了去重和过滤,最终输出“1,2,3”。其中,forEach属于终结操作,流要动起来,必须依赖终结操作
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 2, 3, 4, 5, 6));
list.stream() //把集合转换为流
.distinct() //去重
.filter(arr -> arr < 4) //过滤
.forEach(arr -> System.out.println(arr)); //遍历输出
}
}
3.2 stream流的获取方式
在使用流功能前,必须得到stream流,下面介绍stream流的几种创建方式。
单列集合
集合对象.stream
List<Student> students = getStudents();
Stream<Student> stream = students.stream();
数组
Arrays.stream(数组) || Stream.of()来创建
Integer[] arr = new Integer[]{1, 2, 3};
Stream<Integer> stream = Arrays.stream(arr); //Arrays.stream创建
Stream<Integer> arr1 = Stream.of(arr); //Stream.of()创建
双列集合
转换为单列集合后在创建
Map<String, Integer> map = new HashMap<>();
map.put("乐乐", 18);
map.put("兴兴", 16);
map.put("乐乐", 20);
// 通过键获取stream流
Stream<String> stream = map.keySet().stream();
// 通过键值对获取stream流
Stream<Map.Entry<String, Integer>> stream1 = map.entrySet().stream();
3.3 常见中间操作
- fifter:符合条件,则保留在流当中
- map:对流中的元素进行计算 || 转换(eg: 对流中每个元素,拼接"map"字符)
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 2, 3, 4, 5, 6));
list.stream() //把集合转换为流
.map(arr -> arr + "map") //拼接字符"map"
.forEach(str -> System.out.println(str)); //遍历输出
- distinct:去重,底层依赖Object的equals方法比较,自定义Student类要注意重写equals方法
- sorted:排序,自定义Student类要注意重写compareTo方法 || sorted中实现new Comparator中的compare方法
- limit:设置流的最大长度,超出部分被抛弃(eg:limit(2),存在10个元素,后续8个被抛弃)
- skip:跳过流中的前n个元素,返回剩下元素
- flatMap:把一个对象转换为多个流对象
eg:存在Author对象,Author存在属性List<Book> books。authors中包含3个作家,每个作家的List<Book>中包含二本书。
在flatMap中,以下代码把所有authos的List<Book>,全部转换为Book流对象,此时流存在6个Book对象。flatMap中要求返回的是Steam对象。
// 此时流中6个Book对象
authors.stream()
.flatMap(author -> author.getBooks().stream());
在map中,则只能得到3个List<Book>,map要求返回的是Object对象。
// 此时流中3个对象,每个对象结构是List<Book>
authors.stream()
.map(author -> author.getBooks());
3.4 常见终结操作
- forEach:对元素进行遍历,并可进行操作
- count:获取当前流中的元素个数。count()方法不需要入参,但注意接收返回值
long count = list.stream() //把集合转换为流
.distinct()
.count();//得到元素个数
- max & min:获取流中最值,返回的是Optional对象
Optional<Integer> max = list.stream() //把集合转换为流
.max((o1, o2) -> o1 - o2);// 设置比较规则
- collect:把当前流转换为一个集合。eg:获取一个存放所有作者名字的list集合。
list.stream() //转换为stream流
.map(author -> author.getName()) //map计算得到name属性
.collect(Collectors.toList()); //转换为list集合,Collectors.toList()能够返回collect()所需要的参数类型
转换为set集合:collect(Collectors.toSet())
转换为map集合:collect(Collectors.toMap(author -> author.getName, author -> author.getBooks())
查找与匹配
- anyMatch:是否存在符合匹配条件的元素,结果为boolean类型
- allMatch:所有元素都匹配判断条件,则返回true,否则false
- noneMAtch:都不符合判断条件,则返回true,否则false
- findAny:获取流中的任意一个元素,存在随机性。eg:获取任意一个年龄大于18的作家。
- findFirst:获取流中的第一个元素
reduce归并
对流中的数据按照计算方式计算出一个结果(流中所有元素聚合为一个)
list.stream() //转换为stream流
.map(author -> author.getAge()) //map计算得到name属性
.reduce(0, (res, element) -> res + element); //identity定义res初始值为0,参数二定义累加
如果对reduce中的参数不清楚,可以查看idea提示符,看传参是哪种类型。通过原有new方法,然后再alt + enter简化为lambda表达式。
reduce也可以传一个参数,如上图第二行,即为一个参数,源码中此时默认初始化值为null,后续初始值被赋值为流中第一个元素。
4. stream流下巧用idea
在idea下打断点,点击2,在3中即可得到stream流的详细情况。
以上是lanbda表达式和stream流的相关介绍,详细学习可参考B站视频:lambda与stream流编程的学习
5. stream流技巧
- 没有终结操作,中间操作不会执行
- 流是一次性的,同一个stream对象不能二次使用,需再次list.stream()新建stream对象
- 流不会影响原来集合中的元素(如流中,直接student.setAge,会导致属性改变,原因可查看:Java中只存在值传递,调用函数赋值未修改异常-CSDN博客)