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
使用流进行自定义去重
单属性去重
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));
Stream流、lambda表达式、方法引用、构造引用的更多相关文章
- 函数式接口 & lambda表达式 & 方法引用
拉呱: 终于,学习jdk8的新特性了,初体验带给我的感觉真爽,代码精简的不行,可读性也很好,而且,spring5也是把jdk8的融入到血液里,总之一句话吧,说的打趣一点,学的时候自己难受,学完了写出来 ...
- 5万字长文:Stream和Lambda表达式最佳实践-附PDF下载
目录 1. Streams简介 1.1 创建Stream 1.2 Streams多线程 1.3 Stream的基本操作 Matching Filtering Mapping FlatMap Reduc ...
- [2014-12-30]如何动态构造Lambda表达式(动态构造Lambda查询条件表达式)
声明 本文对Lambda表达式的扩展,示例代码来源于网络. 场景描述 web开发查询功能的时候,如果查询条件比较多,就会遇到动态组合查询条件的情况.在手写sql的情况下,我们一般会根据传入的参数,针对 ...
- 无情的Java 8 之 Stream和lambda表达式篇
不好意思,最近刷小视频刷的有点上头 看到这图就不自觉的要来一句:"卧槽,无情" 好了,我要开始正经了 JAVA 8 已经推出有一段时间了, 相比之前, 我们操作集合的方式应该是这样 ...
- 初识Stream API + Lambda表达式
使用新特性简化代码,增强可读性 package com.gg.java8; import java.util.*; import org.junit.Test; public class TestLa ...
- 009-jdk1.8版本新特性一-展方法,Lambda表达式,函数式接口、方法引用构造引用
一.JDK1.8 名称:Spider(蜘蛛) 发布日期:2014-03-18 新特性: 1.1.扩展方法[接口的默认方法] Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 defaul ...
- java8之lambda表达式&方法引用(一)
本文将简单的介绍一下Lambda表达式和方法引用,这也是Java8的重要更新,Lambda表达式和方法引用最主要的功能是为流(专门负责迭代数据的集合)服务. 什么是lambda表达式 可以把lambd ...
- java8之stream和lambda表达式
JAVA 8 已经推出有一段时间了, 相比之前, 我们操作集合的方式应该是这样? 代码:List<String> list = new ArrayList<>(); list. ...
- 这么简单,还不会使用java8 stream流的map()方法吗?
一.前言 在日常的开发工作中经常碰到要处理list中数据的问题,比如从数据库中查出了很多学生,由于一些原因需要在内存中找出这些学生中的所有姓名,或者把名为"王五"的语文成绩暂时修改 ...
- 黑马Lambda表达式学习 Stream流 函数式接口 Lambda表达式 方法引用
随机推荐
- 图解论文《The Part-Time Parliament》
本文以图文并茂的方式重新演绎 Paxos 开山之作 <The Part-Time Parliament>[1],并尝试解释原论文中语焉不详的地方. 背景 在 Paxos 小岛上,施行着一种 ...
- JZOJ 4212. 【五校联考1day2】我想大声告诉你
题目 解析 设 \(f_{i,j}\) 表示 \(i+1..n\) 个人能受到 \(j\) 次攻击的概率 因为选人出局的顺序是无所谓的,所以我们设从 \(1..n\) 依次选人出局 那么转移时需要分类 ...
- Vulhub 漏洞学习之:Apache HTTPD
Vulhub 漏洞学习之:Apache HTTPD 目录 Vulhub 漏洞学习之:Apache HTTPD 1 Apache HTTPD 换行解析漏洞(CVE-2017-15715) 1.1 漏洞利 ...
- C#泛型接口请求封装类
using HttpUtil; using Newtonsoft.Json; using System; using System.Collections.Generic; using System. ...
- CSS:linear-gradient()背景颜色渐变
css语法 background: linear-gradient(direction,color-stop1,color-stop2,...); direction:用角度值指定渐变的方向(或角度) ...
- JS常用默认行为
form下表示: var forms =document.forms[0]; var forms =document.myform; 找input:var txtName = form.element ...
- Git多分支 远程仓库 协同开发以及解决冲突
目录 一.Git多分支及远程仓库 1.Git多分支 2.正常密码链接远程仓库 3.ssh公钥私钥方式链接远程仓库 三.协同开发及解决冲突 1.协同开发 2.解决冲突 四.线上分支合并及远程仓库回滚 1 ...
- layui级联操作
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <link re ...
- 微信小程序if for
1.控制代码的显示隐藏 1.wx:if="{{}}"判断是否需要渲染代码 <view wx:if="{{tiaojian===1}}">显示1< ...
- 【Redis的三种数据删除策略】定时定期惰性,超出内存就自动清理
https://blog.csdn.net/DQWERww/article/details/126453008 https://blog.csdn.net/qq_38056518/article/de ...