第九章 Lambda表达式和Stream流
9.1Lambda
-
语法
()->{}
-
演变过程 , Lambda本质 -> 就是一个接口中的唯一的那个方法的实现
-
Lambda表达式
Lambda表达式是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使得Java语言表达能力得到了提升。 -
lambda表达式
()->{}
左侧:Lambda表达式的参数列表。对应接口中抽象方法的参数列表。
右侧:Lambda表达式中所需要执行的功能,即Lambda体。对应接口中抽象方法的实现。 -
注意:Lambda表达式需要函数式接口的支持
-
方法定义
1)需求
2)参数: 不确定的数据|有可能会改变的数据定义参数列表上
3)返回值: 方法执行完毕是否需要得到一个结果,需要定义返回值
4)方法名 : 见名知意
9.1.1函数式接口
Lambda的实现依赖于函数式接口,接口可以我们自己定义,当然也可以使用JDK已经提供好的一些函数式接口,这些接口基本已经能够满足我们的常用操作,并且在集合等框架中已经广泛地使用了,所以我们可以直接使用这些接口。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eohNDkhD-1648264558446)(JavaSE初级知识.assets/1648216941996.png)]
-
只有一个必须被重写的抽象方法的接口,java.util.function 包下提供了一系列的函数式接口
-
四大内置函数式接口 :
1**.Consumer< T > 消费型接口**
void accept(T t) 对给定的参数执行此操作。
2**.Function<T,R> 函数型接口**
R apply(T t) 将此函数应用于给定的参数。
3.Supplier< T > 供给型接口
T get() 获取结果。
4.Predicate< T > 段言型接口
boolean test(T t) 根据给定的参数计算此谓词。
public class Class001_FunctionalInterface {
public static void main(String[] args) {
testComsumer(5000, m -> System.out.println("为主播打赏" + m));
testComsumer(10000, m -> System.out.println("今天做全身spa消费" + m));
System.out.println(strHandler(" 哈哈 ", s -> s.trim()));
System.out.println(strHandler(" abc ", s -> s.toUpperCase()));
//[5,10]
System.out.println(testNumRandom(5, () -> (int) (Math.random() * (10 - 5 + 1) + 5)));
System.out.println(testNumRandom(10, () -> (int) (Math.random() * (20 - 10 + 1) + 10)));
System.out.println(testString(List.of("张三", "李四", "张三丰", "欧阳王渊"), s -> s.length() >= 3));
}
//对一个集合中的多个字符串进行某种规则的过滤,返回过滤后的结果
public static List<String> testString(List<String> list, Predicate<String> pre) {
//定义一个List集合,存储满足条件的字符串
List<String> strs = new ArrayList<>();
//遍历原list集合,拿到每一个字符串进行判定
for (String s : list) {
//满足条件字符串放入strs集合
if (pre.test(s)) {
strs.add(s);
}
}
return strs;
}
//功能: 提供指定个数的,指定规则的随机整数
public static List<Integer> testNumRandom(int num, Supplier<Integer> sup) {
//定义一个List集合,存储生成的随机数
List<Integer> list = new ArrayList<>();
//循环num次,每次调用get方法生成一个随机数
for (int i = 1; i <= num; i++) {
list.add(sup.get());
}
//返回结果
return list;
}
//功能: 对一个字符串进行某种行为的操作得到结果
public static String strHandler(String str, Function<String, String> fun) {
return fun.apply(str);
}
//功能: 每天的缴费记录
public static void testComsumer(double money, Consumer<Double> con) {
con.accept(money);
}
}
9.1.2 方法引用和构造器引用
若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”(可以理解为方法引用是Lambda表达式的另一种表现形式),主要有三种语法格式:
- 简化lambda表达式的,是lambda表达式的另外一种变现形式
- 前提 : 当lambda体的实现是通过调用另外一个方法实现的,可以通过方法引用直接引用这个方法,用来简化完整的lambda表达式的结构
- 语法:
对象::实例方法
类名::静态方法
类名::实例方法
- 要求:
- lambda的参数列表与返回值要求能够一一对应lambda体中所引用方法的参数列表与返回值
- lambda的返回值要求与所引用方法的返回值保持对应一致
- lambda的参数列表如果只有一个参数 : 作为调用所引用的成员方法的对象存在
- lambda的参数列表如果存在多个参数 : 第一个参数作为调用所引用的成员方法的对象存在,lambda的第二个参数开始,一一对应匹配所引用方法的参数列表
- 构造器引用:
数据类型::new
public class Class002_MethodQuite {
public static void main(String[] args) {
test4();
}
//构造器引用
public static void test4() {
//1)lambda的参数列表匹配构造器的参数列表,lambda返回值就是所构建的对象--> 构造器引用
//Supplier<Employee> sup= ()->new Employee();
Supplier<Employee> sup = Employee::new;
System.out.println(sup.get());
//Function<String,Employee> fun = (s)->new Employee(s);
Function<String, Employee> fun = Employee::new;
System.out.println(fun.apply("zhangsan"));
}
//类名::实例方法
public static void test3() {
///判断 : 1)lambda体的实现是通过调用另外一个方法实现的 ->√ equals 2)lambda的返回值要求与所引用方法的返回值保持对应一致,lambda的参数列表如果存在多个参数 : 第一个参数作为调用成员方法的对象存在,lambda的第二个参数开始,匹配所引用方法的参数列表 ->对
//BiPredicate<String,String> pre = (x,y) -> x.equals(y);
BiPredicate<String, String> pre = String::equals;
System.out.println(pre.test(new String("nihao"), "nihao"));
;
}
//类名::静态方法
public static void test2() {
//Comparator<Integer> com = (x,y) -> x-y; //不能通过方法引用,因为-是四则运算,不是方法的调用
//判断 : 1)lambda体的实现是通过调用另外一个方法实现的->√ compare 2)lambda的参数列表与返回值要求能够一一对应lambda体中所引用方法的参数列表与返回值 √
//Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
Comparator<Integer> com = Integer::compare;
System.out.println(com.compare(100, 101));
;
}
//对象::实例方法
public static void test1() {
List<Integer> list = List.of(1, 2, 3, 4, 5);
//判断 : 1)lambda体的实现是通过调用另外一个方法实现的->√ println 2)lambda的参数列表与返回值要求能够一一对应lambda体中所引用方法的参数列表与返回值 √
PrintStream ps = System.out;
//Consumer con = i -> System.out.println(i);
//Consumer con = i -> ps.println(i);
Consumer con = ps::println;
list.forEach(con);
list.forEach(ps::println);
list.forEach(System.out::println);
}
}
9.2 Stream流
9.2.1 Stream 流
数据的渠道,用来操作由数据源(数组,集合)所产生的元素序列.
IO : 传输数据
Stream流 : 操作数据,计算数据
数组|集合 : 存储数据
- 特点:
1.Stream流本身不会存储数据
2.Stream不会修改数据源|源对象,每次回返回持有结果的新的流Stream
3.延迟执行|惰性加载 : 当获取终止行为时候,才会执行一系列的中间操作
4.流都是一次性的流,不能重复使用多次,一旦使用过就已经被破坏
9.2.2 Stream流操作步骤:
创建Stream–>中间操作–>终止行为
1. 创建Stream
1) Collection->stream
2) Arrays->stream(数组)
3) Stream.of(值列表)
public class Class001_Stream {
public static void main(String[] args) {
//Collection-->stream()
Stream<Integer> stream = List.of(1,2,3,4,5).stream();
System.out.println(stream);
stream.forEach(System.out::println);
//Arrays->stream(数组)
String[] arr = {"aaa","bbb","ccc"};
Stream<String> stream1 = Arrays.stream(arr);
stream1.forEach(System.out::println);
//Stream.of(值列表)
Stream<Integer> stream2 = Stream.of(5,4,3,2,1);
stream2.forEach(System.out::println);
}
}
2.中间操作
都会返回一个持有结果的新的流
1).过滤 Stream filter(Predicate<? super T> predicate);
2).去重 distinct()
比较equals与hashCode()
3).截取 limit(long) 从第一个开始截取几个
4).跳过 skip(long) 跳过前n个
5).排序 sorted() --> 内部比较器
sorted(Comparator) ->外部比较器
6).映射 map(Function fun) stream操作的每一个数据都所用于参数函数,映射成一个新的结果, 最后返回一个持有所有映射后的新的结果的流
public class Class002_Stream {
public static void main(String[] args) {
List<Employee> list = Arrays.asList(
new Employee("bcd",27,9500),
new Employee("aaa",29,10000),
new Employee("abc",28,8000),
new Employee("bc",28,9000),
new Employee("bc",28,9000),
new Employee("cde",30,12000)
);
//获取Stream
Stream<Employee> stream = list.stream();
//中间操作
//过滤
//stream = stream.filter(e-> e.getAge()>=28);
//流式调用|链式调用
//stream = stream.distinct().limit(3).skip(1);
//排序
//stream = stream.sorted();
stream = stream.sorted((x,y)->Double.compare(y.getSalary(),x.getSalary())); //不能通过方法引用
Stream<String> names = stream.map(e->e.getName()).distinct();
list.stream().map(e->e.getSalary()).distinct().filter(s->s>=10000).sorted().forEach(System.out::println);
//终止行为
//stream.forEach(System.out::println);
names.forEach(System.out::println);
}
}
3.终止行为
1).遍历 foreach(Consumer)
2).查找与匹配
// allMatch-检查是否匹配所有元素
// anyMatch-检查是否至少匹配一个元素
// noneMatch-检查是否没有匹配所有元素
// findFirst-返回第一个元素
// findAny-返回当前流中的任意元素
// count-返回流中元素的总个数
// max-返回流中最大值
// min-返回流中最小值
3).规约 reduce
map->reduce
加工->计算结果
4).收集
collect()
public class Class003_Stream {
public static void main(String[] args) {
List<Employee> list = Arrays.asList(
new Employee("bcd",27,9500),
new Employee("aaa",29,10000),
new Employee("abc",28,8000),
new Employee("bc",28,9000),
new Employee("bc",28,9000),
new Employee("cde",30,12000)
);
//判断每一个员工是否都>=20岁
boolean flag = list.stream().distinct().allMatch(e->e.getAge()>=20);
System.out.println(flag);
//查找薪资最高的员工
//Optional<T> 存储一个数据的容器类型->jdk8新增的容器类型-->帮助避免空指针异常的出现
Optional<Employee> op = list.stream().sorted((x, y)->Double.compare(y.getSalary(),x.getSalary())).findFirst();
System.out.println(op.get());
//parallelStream() 并行流
System.out.println(list.stream().parallel().findAny().get());
System.out.println(list.stream().filter(e->e.getSalary()<=10000).count());
//查找薪资最高的员工
System.out.println(list.stream().distinct().max((x,y)->Double.compare(x.getSalary(),y.getSalary())).get());;
//规约
//找到公司所有员工的薪资,求和
System.out.println(list.stream().map(Employee::getSalary).reduce((x,y)->x+y).get());;
//1+2+3+4+5
Stream<Integer> stream = Stream.of(1,2,3,4,5);
/*System.out.println(stream.reduce((x,y)->{
System.out.println("运算过程 : x = "+x+",y = "+y);
return x+y;
}).get());*/
System.out.println(stream.reduce(100,(x,y)->{
System.out.println("运算过程 : x = "+x+",y = "+y);
return x+y;
}));;
//收集collect
System.out.println(list.stream().distinct().count());
//static <T> Collector<T,?,Long> counting() 返回类型为 T的 Collector接受元素,用于计算输入元素的数量。
System.out.println(list.stream().distinct().collect(Collectors.counting()));
//平均薪资 static <T> Collector<T,?,Double> averagingDouble(ToDoubleFunction<? super T> mapper) 返回 Collector ,它生成应用于输入元素的双值函数的算术平均值。
System.out.println(list.stream().distinct().collect(Collectors.averagingDouble(Employee::getSalary)));
//static <T> Collector<T,?,List<T>> toList() 返回 Collector ,将输入元素累积到新的 List 。
System.out.println(list.stream().filter(e->e.getAge()>=28).collect(Collectors.toList()));
//static <T> Collector<T,?,Set<T>> toSet() 返回 Collector ,将输入元素累积到新的 Set 。
System.out.println(list.stream().filter(e->e.getAge()>=28).collect(Collectors.toSet()));
//static <T,K,U>
//Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper) 返回 Collector ,它将元素累积到 Map其键和值是将提供的映射函数应用于输入元素的结果。
System.out.println(list.stream().distinct().collect(Collectors.toMap(Employee::getName,Employee::getSalary)));
}
}