Stream流、lambda表达式、方法引用、构造引用
函数式接口
函数接口为lambda表达式和方法引用提供目标类型,就是提供支持的接口里面只有且必须只有一个抽象方法,
如果接口只有一个抽象方法,java默认他为函数式接口
@FunctionalInterfafce注解限定只能有一个抽象方法
- 一个函数式接口有且只有一个抽象方法。
- 默认方法不是抽象方法,因为它们已经实现了。
- 重写了超类Object类中任意一个public方法的方法并不算接口中的抽象方法。
@FunctionalInterface规定重写了超类Object类中的任意一个public方法的方法并不算入该接口中的抽象方法计数当中,因为该接口的任何实
现都将具有java.lang.Object或其他地方的实现
内置四大核心函数式接口
其他接口
eg:
@FunctionalInterface public interface Consumer<T> 接受单个输入参数且没有返回值。和其他大部分函数式接口不同,Consumer被期望用于操作副作用 Represents an operation that accepts a single input argument and returns no result. Unlike most other functional interfaces, Consumer is expected to operate via side-effects. This is a functional interface whose functional method is accept(Object). @FunctionalInterface public interface Supplier<T> 表示结果的提供者 Represents a supplier of results. 不需要每次调用时返回新的或者不同的结果 There is no requirement that a new or distinct result be returned each time the supplier is invoked. 该函数式接口的方法时get() This is a functional interface whose functional method is get(). @FunctionalInterface public interface Function<T,R> 接受一个参数并返回一个结果 Represents a function that accepts one argument and produces a result. This is a functional interface whose functional method is apply(Object). @FunctionalInterface public interface Predicate<T> 对一个参数的断言 Represents a predicate (boolean-valued function) of one argument. This is a functional interface whose functional method is test(Object).
1 public class FunctionUse { 2 public static void main(String[] args) { 3 //NumberFormatException 4 //System.out.println(Integer.valueOf("a")); 5 //只能作用域数字型的字符串 6 //System.out.println(Integer.valueOf("11")); 7 //NumberFormatException 8 //System.out.println(Integer.parseInt("a")); 9 Consumer<String> consumer =t->{ 10 System.out.println("消费者型函数式接口,有参数,无返回值"); 11 System.out.println("参数是"+t); 12 }; 13 consumer.accept("abc"); 14 Supplier<String> supplier = ()->{ 15 System.out.println("无参数,有返回值"); 16 return "a result"; 17 }; 18 supplier.get(); 19 20 Function<String , Integer> function = (String input)->{ 21 System.out.println("有参数,有返回值"); 22 return input.length(); 23 }; 24 System.out.println(function.apply("Abc")); 25 26 Predicate<String> predicate = t->{ 27 return true; 28 }; 29 System.out.println(predicate.test("小仙女")); 30 }
Lambda表达式
只有函数式接口才能使用lambda表达式
允许把函数作为一个方法的参数传递
可以当成匿名函数使用
使代码变得更加简洁紧凑
语法:

方法引用
若lambda方法提中的功能已经提供了方法实现,可以使用方法引用=》可以理解为lambda表达式的另一种形式
形式
*一:对象::实例方法名
*二:类名::静态方法名
*三(特殊):类名::实例方法名 条件是第一个参数为lambda体的方法调用者
注意:
*①(前两种形式)方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!(最重要,同样适用于构造引用)
*②(特殊形式)若Lambda的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式:ClassName::MethodName
1 public class MethodReferenceTest { 2 /** 3 * @description 对象 :: 实例方法名 4 */ 5 @Test 6 public void test1(){ 7 PrintStream ps = System.out; 8 Consumer<String> con = x -> ps.println(x); 9 //hello 10 con.accept("hello"); 11 12 PrintStream ps1 = System.out; 13 Consumer<String> con1 = ps1::println; 14 //world 15 con1.accept("world"); 16 17 Consumer<String> con3 = System.out::println; 18 //hello world 19 con3.accept("hello world"); 20 } 21 @Test 22 public void test2(){ 23 Teacher teacher1 = new Teacher("王尼玛",600,"京山小学"); 24 Supplier<String> supplier1 = () -> teacher1.getName(); 25 System.out.println(supplier1.get()); 26 System.out.println("--------------------------"); 27 28 Supplier<Integer> supplier2 = teacher1 :: getAge; 29 System.out.println(supplier2.get()); 30 System.out.println("--------------------------"); 31 32 Supplier<String> supplier3 = new Teacher("王尼玛",600,"京山小学") ::getName; 33 System.out.println(supplier3.get()); 34 } 35 /** 36 * @description 类名 :: 静态方法名 37 */ 38 @Test 39 public void test3(){ 40 //Integer 的compare()方法是静态方法 41 Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y); 42 System.out.println(comparator.compare(2,3)); 43 44 Comparator<Integer> comparator1 = Integer::compare; 45 System.out.println(comparator1.compare(4,2)); 46 } 47 /** 48 * @description 类名 :: 实例方法名 49 */ 50 @Test 51 public void test4(){ 52 //x为lambda体中的调用者 53 BiPredicate<String,String> bp = (x,y) -> x.equals(y); 54 System.out.println(bp.test("abc","ABC")); 55 System.out.println("--------------"); 56 57 BiPredicate<String,String> bp1 = String::equals; 58 System.out.println(bp1.test("abc","cba")); 59 System.out.println("-----------"); 60 //参数e为调用者 61 Function<Teacher,String> function = e -> e.show(); 62 System.out.println(function.apply(new Teacher())); 63 System.out.println("----------------"); 64 65 Function<Teacher,String> function1 = Teacher::show; 66 System.out.println(function1.apply(new Teacher())); 67 68 //不是静态方法,也没有参数 不能使用 类名:: 方法名 的形式 Non-static method cannot be referenced from a static context 69 // Supplier<String> supplier1 = Teacher::show; 70 } }
构造引用
/**
*形式:
*对象名::new
*@author夜神
*@notice 最重要的一点,和方法引用一样,需要和函数式接口的抽象方法的返回值类型、参数列表一致
*
*数组构造引用
*类型[]::new
*就是对象::方法的形式
*集合构造引用
*类型::new
*
*/
1 public class ConstructorReference { 2 @Test 3 public void test5(){ 4 Function<Integer, List<String>> function = ArrayList::new; 5 List<String> stringList = function.apply(5); 6 } 7 8 /** 9 * 数组引用 10 * @description 11 * 形式: 类型[] :: new 12 * new String[size] ->一个参数一个返回值(String[]对象)->Function接口 13 */ 14 @Test 15 public void test4() { 16 //lambda表达式 17 Function<Integer, String[]> function = args -> new String[args]; 18 String[] str = function.apply(3); 19 System.out.println(str.length); 20 21 //数组引用 22 Function<Integer, String[]> function2 = String[] :: new; 23 String[] str2 = function2.apply(4); 24 System.out.println(str2.length); 25 } 26 27 /** 28 * @description 无参构造 29 * @because Supplier的抽象方法参数列表为空 ->无参数,有返回值 和无参构造一致 30 */ 31 @Test 32 public void test() { 33 //lambda表达式的写法 34 Supplier<Teacher> supplier = () -> new Teacher(); 35 System.out.println(supplier.get()); 36 //升级,构造引用 37 Supplier<Teacher> supplier1 = Teacher::new; 38 System.out.println(supplier1.get()); 39 } 40 41 /** 42 * 单参构造 43 * 44 * @desctiption Function 支持一个参数,一个返回值 45 */ 46 @Test 47 public void test2() { 48 //lambda表达式 49 Function<String, Teacher> function = x -> new Teacher(); 50 System.out.println(function.apply("王尼玛")); 51 52 //构造引用 53 Function<String, Teacher> function1 = Teacher::new; 54 System.out.println(function1.apply("王尼玛")); 55 } 56 57 /** 58 * 双参构造 59 * 60 * @description BiFunction支持两个参数和一个返回值 61 */ 62 @Test 63 public void test3() { 64 //lambda表达式 65 BiFunction<String, Integer, Teacher> function = (x, y) -> new Teacher(x, y); 66 System.out.println(function.apply("狗东西", 600)); 67 68 //构造引用 69 BiFunction<String, Integer, Teacher> biFunction = Teacher::new; 70 System.out.println(biFunction.apply("王尼玛", 600)); } }
Stream流
Classes to support functional-style operations on streams of elements, such as map-reduce transformations on collections.
类,以支持元素流上的函数式操作,例如集合上的映射-还原转换。
Stream 流提供了惰性计算和并行处理的能力,在使用并行计算方式时数据会被自动分解成多段然后并行处理,最后将结果汇总。所以 Stream 操作可以让程序运
行变得更加高效。
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
- Stream自己不会存储元素
- Strream不会改变源对象,相反,他们会返回一个持有结果的新Stream
- Stream的操作是延迟执行的,这意味着他们会等到需要结果时才执行
Stream的操作步骤:
创建Stream:一个数据源,获取一个流
中间操作:一个中间操作链,对数据源的数据进行处理
终止操作:执行中间操作链,并产生结果
创建Stream流的方式
集合提供的Stream()方法
Collectin接口提供了Stream()的默认实现
通过Arrays数组工具创建流
Arrays数组工具提供了静态的创建流的方法
通过Stream类提供的方法创建流
of方法创建指定流
*/ public static<T> Stream<T> of(T t) { return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false); } /** * Returns a sequential ordered stream whose elements are the specified values. * * @param <T> the type of stream elements * @param values the elements of the new stream * @return the new stream */ @SafeVarargs @SuppressWarnings("varargs") // Creating a stream from an array is safe public static<T> Stream<T> of(T... values) { return Arrays.stream(values); }
iterate和generate无限流
//迭代 //public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println); //生成 Stream.generate(Math::random).limit(10).forEach(System.out::println);
常用中间操作
• map:通过一个 Function 把一个元素类型为 T 的流转换成元素类型为 R 的流。对每一个元素进行转换操作
• flatMap:通过一个 Function 把一个元素类型为 T 的流中的每个元素转换成一个元素类型为 R 的流,再把这些转换之后的流合并。对每一个元素进行转换操作,再平铺
• filter:过滤流中的元素,只保留满足由 Predicate 所指定的条件的元素。
• distinct:使用 hashCode()与equals ()方法来删除流中的重复元素。 去重
• limit:截断流使其最多只包含指定数量的元素。 限制个数
• skip:返回一个新的流,并跳过原始流中的前 N 个元素。
• sorted:对流进行排序。
• peek:返回的流与原始流相同。当原始流中的元素被消费时,会首先调用 peek 方法中指定的 Consumer 实现对元素进行处理。多用于调试,IDEA可能提示将map变为peek,实际不建议这样做
• dropWhile:从原始流起始位置开始删除满足指定 Predicate 的元素,直到遇到第一个不满足 Predicate 的元素。
• takeWhile:从原始流起始位置开始保留满足指定 Predicate 的元素,直到遇到第一个不满足 Predicate 的元素。
map和flatMap
map 返回一个包含每个元素经过函数操作后产生的对象的流
flatMap 将进每个元素过函数操作后产生的新的对象转换成流,将这些流平铺连接成一个流,而且去除null值 (提取数据合并起来放入一个流中) so用flatMap能进行一对多操作
常用终止操作
foreach:遍历执行
reduce:进行递归计算
collect:生成新的数据结构
执行流程
default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); }
返回ReferencePipeline.Head实例
ReferencePipeline实际上是一个双向链表的数据结构。而ReferencePipeline对Stream的操作做了实现,每一个中间操作都会返回一个Stream对象,实际上就是ReferencePipeline对象,因此可以得到结论:Stream底层是通过双向链表来实现的。
源阶段返回的是ReferencePipeline.Head对象,而中间操作阶段返回的是ReferencePipeline对象。在流的源阶段和中间阶段仅仅只是返回了ReferencePipeline对象,并没有做其他的方法调用操作,这也是为什么流在执行中间操作并不会有任何的输出或者结果产生。
eg
1 /** 2 * 筛选与切片 3 * filter——接收 Lambda , 从流中排除某些元素。 4 * limit——截断流,使其元素不超过给定数量。 5 * skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 6 * distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 7 * map——映射,接收 Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 8 * 9 */ 10 //filter 过滤 11 @Test 12 public void test4(){ 13 Stream<User> stream = userList.stream() 14 .filter(x -> { 15 System.out.println("中间过滤操作"); 16 return x.getId()>12; 17 }); 18 stream.forEach(System.out::println); 19 } 20 //limt 截流 21 @Test 22 public void test5(){ 23 Stream<User> stream = userList.stream() 24 .filter(x -> x.getId()>12) 25 .limit(2);//只要两个,满足后不会再迭代 e g 26 stream.forEach(System.out::println); 27 } 28 //skip跳过 29 @Test 30 public void test6(){ 31 Stream<User> stream = userList.stream() 32 .filter(x -> x.getId()>12) 33 .skip(1)//把e跳过了 34 .limit(2);//g d 35 stream.forEach(System.out::println); 36 } 37 //distinct 去重 38 @Test 39 public void test7(){ 40 System.out.println(userList); 41 42 userList.stream() 43 .distinct() 44 .forEach(System.out::println); 45 } 46 /** 47 * map 映射 48 * Returns a stream 49 * consisting of the results 50 * of applying the given function 51 * to the elements of this stream. 52 * 返回一个Stream,包含所有被函数作用后产生的新值。元素个数一一对应。 53 *接收 Lambda , 将元素转换成其他形式或提取信息。 54 * 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 55 * 56 * flatMap 平面映射 57 * 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流(平铺)连接成一个流 58 * 元素个数可能更多,因为把所有流中的值连接成一个流,而一个流中可能有多个值 59 */ 60 @Test 61 public void test8(){ 62 //map 返回一个包含每个元素经过函数操作后产生的对象的流 63 Stream<Object> objectStream = userList.stream() 64 .distinct() 65 .map(x -> { 66 if (x.getId() > 11) { 67 List<Object> list = new ArrayList<>(); 68 list.add(x); 69 return list; 70 } 71 return null; 72 });//返回的是Stream<R> R:返回值类型 73 //[null, [User{id=13, name='e', age=25}], [User{id=15, name='g', age=28}], [User{id=18, name='d', age=30}]] 74 System.out.println(objectStream.collect(Collectors.toList())); 75 System.out.println("------------------------------"); 76 //flatMap 将进每个元素过函数操作后产生的新的对象转换成流,将这些流平铺连接成一个流,而且去除null值 77 Stream<Object> objStream = userList.stream() 78 .distinct() 79 .flatMap(x -> { 80 if (x.getId()>11){ 81 List<Object> list = new ArrayList<>(); 82 list.add(x); 83 return list.stream(); 84 } 85 return null; 86 }); 87
使用流分组去重并过滤数据
package org.example;
import java.util.*;
import java.util.stream.*;
class Record {
private String name;
private Date date;
public Record(String name, Date date) {
this.name = name;
this.date = date;
}
public String getName() {
return name;
}
public Date getDate() {
return date;
}
}
class GroupAndLatestExample {
public static void main(String[] args) {
List<Record> records = Arrays.asList(
new Record("Alice", new Date(1000)),
new Record("Bob", new Date(2000)),
new Record("Alice", new Date(3000)),
new Record("Bob", new Date(4000))
);
Map<String, Optional<Record>> latestDataPerName = records.stream()
.collect(Collectors.groupingBy(
Record::getName,
Collectors.maxBy(Comparator.comparing(Record::getDate))
));
List<Record> latestDataList = latestDataPerName.values().stream()
.map(Optional::get)
.collect(Collectors.toList());
// 打印结果
latestDataList.forEach(data -> System.out.println(data.getName() + " - " + data.getDate()));
}
}
使用流进行自定义去重
单属性去重
List<Student> collect = list.stream().collect( Collectors.collectingAndThen( Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(People::getName))), ArrayList::new));
多属性去重
通常多个属性值之间用“;”隔开
1 ArrayList<Student> collect1 = list.stream().collect(collectingAndThen( 2 toCollection(() -> new TreeSet<>( 3 Comparator.comparing(element -> element.getName() + ";" + element.getAge()))), ArrayList::new));
使用流进行自定义排序
List<People> peopleList = getPeopleList(); 7 // 正序 8 List<People> sortPeople = peopleList.stream() 9 .sorted(Comparator.comparing(People::getName)).collect(Collectors.toList()); 10 // [People(name=张三, age=23), People(name=张三, age=20), People(name=李四, age=20)] 11 System.out.println(sortPeople); 12 // 反序 reversed() 13 List<People> reSortPeople = peopleList.stream() 14 .sorted(Comparator.comparing(People::getName).reversed()).collect(Collectors.toList()); 15 // [People(name=李四, age=20), People(name=张三, age=23), People(name=张三, age=20)] 16 System.out.println(reSortPeople); 17 // 组合排序 comparing().thenComparing() 18 List<People> combineSort = peopleList.stream() 19 .sorted(Comparator.comparing(People::getName).thenComparing(People::getAge)).collect(Collectors.toList()); 20 // [People(name=张三, age=20), People(name=张三, age=23), People(name=李四, age=20)] 21 System.out.println(combineSort);
使用流转List为Map
Collectors下有toMap方法
public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) { return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new); }
// mergeFunction:解决相同key的碰撞 public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction) { return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new); }
public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier) { BiConsumer<M, T> accumulator = (map, element) -> map.merge(keyMapper.apply(element), valueMapper.apply(element), mergeFunction); return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID); }
声明一个集合
//声明一个List集合
List<People> list = new ArrayList<>();
list.add(new People("张三", 22));
list.add(new People("张三", 23));
list.add(new People("李四", 24));
List转Map
Map<String, Integer> map = list.stream().collect(Collectors.toMap(People::getName, People::getAge));
key重复,报错
一般会碰到两个问题,key重复和空指异常 key重复可以选用,空指异常就在转换前保证没有null
key重复问题
选用后面的key (根据业务需求)
// key重复,选用后者 Map<String, Integer> map = list.stream().collect(Collectors.toMap(People::getName, People::getAge, (key1, key2) ->key2));
空指异常问题
转换前就保证没有null
转换时进行判断处理
Map<String, Integer> map = list.stream().collect(Collectors.toMap(People::getName, p->p.getAge()==null?0:p.getAge(), (key1, key2) ->key2));
作者: deity-night
出处: https://www.cnblogs.com/deity-night/
关于作者:码农
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 如有问题, 可邮件(***@163.com)咨询.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用