Lambda表达式

基本语法

Lambda表达式由3个部分组成:参数部分、->、逻辑部分

image-20250712204043986

整个Lambda表达式就是一个函数对象。

方法引用

基本语法

image-20250712204847531

image-20250712205832503

类型

类名::静态方法

image-20250712223911302

类名::非静态方法

image-20250712224448120

对象::非静态方法

image-20250712224752062

类名::new

image-20250712225842420

以上Student::new虽然长相一样,但是可以通过类型来区分。

image-20250712230130847

this::非静态方法(3的特例,只能在类内部使用)

super::非静态方法(3的特例,只能在类内部使用)

函数接口

如果两个函数对象满足以下要求,那么就可以将他们归为一类,抽象为一个函数式接口。

image-20250712210704891

在接口上方打上@FunctionalInterface注解以便在编译阶段检查接口是否满足条件有且仅有一个抽象方法。

image-20250712210822402

常见函数接口

image-20250712214146174

命名规则

image-20250712214302842

!!!特例

image-20250712231224254

闭包

形如(int y) -> x + y与它的外部变量形成了闭包。

image-20250713155556593

!!!注意:变量x必须要是final或者effective final(虽然没有定义为final,但是后文并没有x进行过修改)。但是对象内部的值是可以修改的。

image-20250713155829817

image-20250713155831959

柯里化

让接受多个参数的函数转换成一系列只接受一个参数的函数的过程叫做柯里化。

例子如下:

image-20250713161533968

image-20250713161200812

Stream流

!!!特性

一次使用

两类操作(中间操作 lazy 懒惰,终结操作 eager 迫切)

一次使用

Stream<Integer> stream = Stream.of(1, 2, 3);
stream.forEach(System.out::println);
stream.forEach(System.out::println); //报错,因为流中数据已经被消费完了,无法被再次消费

两类操作

image-20250717001618569

过滤filter

    Stream.of(
new Student("jack", 18, "男"),
new Student("marry", 20, "女"),
new Student("tom", 15, "男")
).filter(stu -> stu.getAge() <= 18)
.forEach(System.out::println);

}

image-20250714211045960

映射map

    Stream.of(
new Student("jack", 18, "男"),
new Student("marry", 20, "女"),
new Student("tom", 15, "男")
).map(stu -> stu.getName() + "xixi")
.forEach(System.out::println);
}

image-20250714211025319

降维(扁平化映射)flatMap

Stream.of(
List.of(new Student("jack", 18, "男"),
new Student("marry", 20, "女"),
new Student("tom", 15, "男")),
List.of(new Student("black", 18, "男"),
new Student("sam", 20, "女"),
new Student("sim", 15, "男"))
)
.flatMap(list -> list.stream())
.forEach(System.out::println);

image-20250714211438621

将外部的list的去除,得到list里面的数据。

构建流

用已有数据构建出Stream对象。

方式有以下三种:

image-20250714211651254

集合构建流

//集合构建(Collection及其子类)
List.of(1,2,3).stream();

//map特殊处理
Map.of("a",1,"b",2).entrySet().stream();

数组构建流

//数组构建
int[] array = new int[]{1,2,3};
Arrays.stream(array);

对象构建流

Stream.of(
List.of(new Student("jack", 18, "男"),
new Student("marry", 20, "女"),
new Student("tom", 15, "男")),
List.of(new Student("black", 18, "男"),
new Student("sam", 20, "女"),
new Student("sim", 15, "男"))
)

流的合并concat

Stream<Integer> stream1 = Stream.of(1, 2, 3);
Stream<Integer> stream2 = Stream.of(4, 5, 6);
//合并
Stream<Integer> concat = Stream.concat(stream1, stream2);
concat.forEach(System.out::println);

截取

skip(long n) 跳过n个,保留剩余部分。

concat.skip(2).forEach(System.out::print);

limit(long n) 保留前n个数据,剩余的舍弃。

concat.limit(2).forEach(System.out::print);

takeWhile(Predicate p) 条件成立保留,一旦条件不成立,剩下的不要。

concat.takeWhile(x -> x < 3).forEach(System.out::print);

dropWhile(Predicate p) 条件成立舍弃,一旦条件不成立,剩下的保留。

concat.dropWhile(x->x >= 3).forEach(System.out::print);

生成流

不用现有数据生成Stream对象。

方式有以下三种:

image-20250714222751669

IntStream.range(start,end)

IntStream.range(1,10).forEach(System.out::print); //左闭右开
IntStream.rangeClosed(1,10).forEach(System.out::print);//闭区间

IntStream.iterate(初始值,生成式) 或者IntStream.iterate(初始值,判断条件,生成式)

IntStream.iterate(1, x -> x + 2).limit(5).forEach(System.out::print);
IntStream.iterate(1, x -> x <= 9 ,x -> x + 2).forEach(System.out::print);

IntStream.generate()

IntStream.generate(() -> ThreadLocalRandom.current().nextInt(100)).limit(5).forEach(System.out::println);//生成100以内的随机数

//随机数生成的另一种方法 ThreadLocalRandom.current().ints(流的大小,下限,上限)
ThreadLocalRandom.current().ints(5,0,100).forEach(System.out::println);

查找

findFirst() 找到第一个

System.out.println(Stream.of(1, 2, 3, 4, 5, 6)
.filter(x -> (x & 1) == 0)
.findFirst() //返回的是Optional对象
.orElse(-1)); //不存在则返回-1

Stream.of(1, 2, 3, 4, 5, 6)
.filter(x -> (x & 1) == 0)
.findFirst()
.ifPresent(System.out::println); //如果存在则打印

findAny() 找到任意一个

Stream.of(1, 2, 3, 4, 5, 6)
.filter(x -> (x & 1) == 0)
.findAny()
.ifPresent(System.out::println);

判断

anyMatch() 只要有任意一个符合,则返回true

System.out.println(Stream.of(1, 2, 3, 4, 5, 6)
.anyMatch(x -> (x & 1) == 0)); //true

allMatch() 全部符合才返回true

System.out.println(Stream.of(1, 2, 3, 4, 5, 6)
.allMatch(x -> (x & 1) == 0)); //false

noneMatch() 都不符合才返回true

System.out.println(Stream.of(1, 2, 3, 4, 5, 6)
.noneMatch(x -> (x & 1) == 0));

去重

distinct()

Stream.of(1,2,3,4,1,2,4,5,7,6,8)
.distinct()
.forEach(System.out::print);

排序

sorted()

      Stream.of(3,5,6,1,2,5,7,9,3,2)
.sorted((a,b) -> a < b ? -1 : a == b ? 0 : 1) //升序排序,返回-1说明a比b小,a排在前,否则b在前
.forEach(System.out::print);

//优化后的代码
Stream.of(3,5,6,1,2,5,7,9,3,2)
.sorted(Comparator.comparingInt(a -> a))
.forEach(System.out::print);

//降序排序
Stream.of(3,5,6,1,2,5,7,9,3,2)
.sorted(Comparator.comparingInt(a -> (int) a).reversed())
.forEach(System.out::print);

image-20250716220500929

以strength降序排序,在strength相同时以名字升序排序。

化简 reduce

两两合并

.reduce((p,x)->r).reduce(init,(p,x)->r) 其中p为上次的合并结果,x为当前元素,r为本次合并结果,init为初始值。

System.out.println(Stream.of(
new Hero("张三", 90),
new Hero("李四", 100),
new Hero("王五", 89),
new Hero("刘子", 78),
new Hero("八爷", 69)
).reduce((h1, h2) -> h1.strength > h2.strength ? h1 : h2)); //返回值为optional

System.out.println(Stream.of(
new Hero("张三", 90),
new Hero("李四", 100),
new Hero("王五", 89),
new Hero("刘子", 78),
new Hero("八爷", 69)
).reduce(new Hero("-",-1),(h1, h2) -> h1.strength > h2.strength ? h1 : h2)); //返回值为Hero,如果流中没有数据,则默认返回Hero("-",-1)

底层为reduce的一些方法,求最值、平均数、数量等。

System.out.println(Stream.of(
new Hero("张三", 90),
new Hero("李四", 100),
new Hero("王五", 89),
new Hero("刘子", 78),
new Hero("八爷", 69)
).count());

System.out.println(Stream.of(
new Hero("张三", 90),
new Hero("李四", 100),
new Hero("王五", 89),
new Hero("刘子", 78),
new Hero("八爷", 69)
).max(Comparator.comparingInt(Hero::strength)));

收集器

Stream.of("张胜男","李四","王五","刘子","八爷")
.collect(Collectors.toList())
.forEach(System.out::println);

Stream.of("张胜男","李四","王五","刘子","八爷")
.collect(Collectors.toSet())
.forEach(System.out::println);

Stream.of("张胜男", "李四", "王五", "刘子", "八爷")
.collect(Collectors.toMap(name -> name, name -> 1))
.forEach((key,value) -> System.out.println(key + ":" + value));

实际应用中Collectors.toMap基本很少用,常用的是Collectors.groupingBy()来做分组需求

	 //以名字长度作为分组条件,长度相同的为一组
Stream.of("张胜男", "李四", "王五", "刘子", "八爷")
.collect(Collectors.groupingBy(String::length,Collectors.toList()))
.forEach((key,value) -> System.out.println(key+":"+value));

image-20250716230340923

下游收集器(配合Collectors.groupingBy()使用)

       
//Collectors.mapping:做转换,将hero对象转换为需要的数据,然后收集到list集合中
Stream.of(
new Hero("张胜男", 90),
new Hero("李四", 100),
new Hero("王五", 89),
new Hero("刘子", 78),
new Hero("八爷", 69)
).collect(Collectors.groupingBy(h -> h.name.length(), Collectors.mapping(h -> h.strength, Collectors.toList())))
.forEach((key, value) -> System.out.println(key + " : " + value));
        
//按名字长度分组,但是要将名字拆分为一个个字符
//.chars():将字符串中的每个字符所对应的ASCII码值放到整数流中
Stream.of(
new Hero("张胜男", 90),
new Hero("李四", 100),
new Hero("王五", 89),
new Hero("刘子", 78),
new Hero("八爷", 69)
).collect(Collectors.groupingBy(h -> h.name.length(), Collectors.flatMapping(h -> h.name.chars().mapToObj(Character::toString),Collectors.toList())))
.forEach((key, value) -> System.out.println(key + " : " + value));

结果如下:image-20250716234036295

//按名字长度分组,求每组包含多少个元素
Stream.of(
new Hero("张胜男", 90),
new Hero("李四", 100),
new Hero("王五", 89),
new Hero("刘子", 78),
new Hero("八爷", 69)
).collect(Collectors.groupingBy(h -> h.name.length(), Collectors.counting()))
.forEach((key, value) -> System.out.println(key + " : " + value));
//按名字长度分组,求每组strength的和        
Stream.of(
new Hero("张胜男", 90),
new Hero("李四", 100),
new Hero("王五", 89),
new Hero("刘子", 78),
new Hero("八爷", 69)
).collect(Collectors.groupingBy(h -> h.name.length(), Collectors.mapping(h -> h.strength,Collectors.reducing(0,(p,x) -> p + x))))
.forEach((key, value) -> System.out.println(key + " : " + value));

image-20250717000232926

image-20250717000241502

基本流

IntStreamLongStreamDoubleStream