函数式编程 参考文章:函数式编程_春风一慕的博客-CSDN博客_函数式编程 特点: 1. 纯函数:函数的执行没有副作用;返回值仅依赖输入参数 2. 高阶函数:函数的参数可以是一个或多个函数;函数的返回值也可以是一个函数 3. 兰姆达表达式;方法引用 package com.example.demo; import org.assertj.core.util.Lists; import javax.sound.midi.SysexMessage; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 参考:https://blog.csdn.net/EQuaker/article/details/86561781 * https://www.cnblogs.com/yunqing/p/9221117.html * 1. 函数式编程:(两个特点) * 可以把函数作为参数传递给另一个函数,也就是所谓的高阶函数 * 可以返回一个函数,这样就可以实现闭包或者惰性计算(闭包:A函数返回B函数,B函数使用了A中的变量,则相当于扩大了A中变量的作用范围;放回方法,可以延迟执行) * 2. 函数式接口(只有一个抽象方法,可以有多个默认实现方法或静态方法) * Consumer<T>:消费性接口 * Supplier<T> 供给型接口 * Function<T,R> 函数型接口 * Predicate<T> 断言型接口 * Comparator<T> 比较器接口,一般用于集合、流中的对象排序等操作中自定义比较规则,(a,b)-> a-b ;a是后一个元素,b是前一个元素,满足计算结果大于0时保留原顺序,小于0时交换顺序 * 3. 兰姆达表达式(函数式接口的实现) * (参数,一个时可省略括号) -> {函数实现,一条语句可以不用括号及返回return} * 4. 方法引用(进一步简化lambda表达式的声明) * objectName::instanceMethod * ClassName::staticMethod * ClassName::instanceMethod (把lambda表达式的第一个参数当成instanceMethod的目标对象,其他剩余参数当成该方法的参数。) * 5. 流stream * 1).Stream是元素的集合,这点让Stream看起来用些类似Iterator; * 2).可以支持顺序和并行的对原Stream进行汇聚的操作;(直接告诉程序要做什么,如转换成大写,怎么做(如并行还是串行等)让计算机自己决定) * 使用步骤:stream创建、转换、聚合(Reduce) * (转换操作都是lazy的,多个转换操作只会在汇聚操作(见下节)的时候融合起来,一次循环完成。 * 我们可以这样简单的理解,Stream里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中, * 在汇聚操作的时候循环Stream对应的集合,然后对每个元素执行所有的函数。) * (汇聚操作(也称为折叠)接受一个元素序列为输入,反复使用某个合并操作,把序列中的元素合并成一个汇总的结果。 * 比如查找一个数字列表的总和或者最大值,或者把这些数字累积成一个List对象。Stream接口有一些通用的汇聚操作, * 比如reduce()和collect();也有一些特定用途的汇聚操作,比如sum(),max()和count()。) * */ public class TestLamda { public static void main(String[] args){ // testStream(); testConsumer(10,(i) -> System.out.println(i*10)); } static void testConsumer(int i,Consumer<Integer> consumer){ consumer.accept(i); } public static void testStream(){ //1. 创建:Collection(集合子类).stream();Stream.of;generate;iterate //2. 转换:distinct;filter;map;flatMap;peek;limit;skip; //3. 汇聚: // 1)可变汇聚:collect 2)其他汇聚:reduce;count;allMatch;anyMatch;findFirst;noneMatch;max;min List<Integer> nums = Lists.newArrayList(1,null,3,4,null,6); Stream<Integer> integerStream = Stream.of(1, 2, 3, 5); Stream.generate(() -> Math.random()); Stream.generate(Math::random); Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println); System.out.println("result: "+nums.stream().filter(s -> s == null).count()); System.out.println("sum is:"+nums.stream().filter(num -> num != null).distinct().mapToInt(num -> num * 2) .peek(System.out::println).skip(2).limit(4).sum()); //汇聚 List<Integer> nums2 = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10); List<Integer> numsWithoutNull = nums2.stream().filter(num -> num != null). collect(() -> new ArrayList<Integer>(),//生成容器 (list, item) -> list.add(item),//元素添加到容器 (list1, list2) -> list1.addAll(list2));//多个结果容器合并操作 List<Integer> numsWithoutNull2 = nums2.stream().filter(num -> num != null). collect(Collectors.toList()); List<Integer> ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10); System.out.println("ints sum is:" + ints.stream().reduce((sum, item) -> sum + item).get()); } }
反应式流
* 是一种基于数据流和变化传递的声明式的编程范式。流是数据的一种存在形态,可以基于流进行处理(类似于流水线),jdk8的stream主要关注的是流的创建、转换、汇聚,也是声明式编程的思想,相比于命令式编程(传统的通过顺序、分支、循环等控制处理过程),声明式编程只需要开发人员告诉程序数据应该做何种处理(如map\reduce),至于如何去处理(并行穿行、什么时机处理等等),不需要开发人员关心。反应式是一种消息驱动的异步处理模式,采用背压模型,谁慢谁主动,可以再push和pull之间切换,消费者慢于生产者时,消费者主动pull,生产者慢于消费者时,生产者push消息,采用这种模型不会使得过快的生产者压制消费者。
* 反应式流一是管理跨异步边界的流数据交换(即数据在线程间传递),二是避免消息消费者缓存任意数量的数据(引入背压模式)。
* 异步处理时(消息驱动),当消费者能力大于生产者效率时,消费者只需等待不会影响系统的正常运行。生产者效率大于消费者时,有两种处理方案:1.改变消费者,消费者引入无界缓冲区,实际上无法实现;2.改变生产者,降低生产者的效率,以避免较快的生产者不会压制消费者,这就是背压模式。
* jdk1.9在juc的flow下引入了反应式流的四大接口,但官方不推荐使用,可以认为是更高版本的特性。从代码结构上看,反应式流包含reactive-streams
和reactive-streams-tck两部分,reactive-streams是一组接口规范,包含了包含反应式流必要的操作和实体,tck是技术兼容包,为实现reactive-stream api提供帮助。1.9反应式流和1.8stream均是声明式编程,1.8stream是同步的,1.9反应式流是异步的,侧重于消息的产生和消费。
* 反应式流是一种编程范式,目前具体实现的框架有rxjava2(兼容了反应式流和rxjava,使用麻烦),Reactor是基于反应式流规范的反应式编程库,是spring5反应式编程(webflux)的基础,由于1.8兰姆达表达式的特性可以
发挥Reactive Streams规范的强大特性,当前reactor要求jdk最低1.8。jdk1.9Flow也是jdk对反应式流规范的一种实现
* Webflux(Reactor)中的Mono、Flux都是Publisher的实现,Mono包含0-1个元素,Flux包含0-N个元素
* reactive-stream api包含四个接口:
//发布者
public interface Publisher < T > {
public void subscribe(Subscriber <?super T > s);
}
//订阅者
public interface Subscriber < T > {
public void onSubscribe(Subscription s);
public void onNext(T t);
public void onError(Throwable t);
public void onComplete();
}
//表示Subscriber消费Publisher发布的一个消息的生命周期
public interface Subscription {
public void request(long n);
public void cancel();
}
//处理器,表示一个处理阶段,它既是订阅者也是发布者,并且遵守两者的契约
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}
* 执行过程:
Publisher,即发布者,是有序消息的生产者。它根据收到的请求向订阅者发布消息。
Subscriber,即订阅者,从发布者那里订阅并接收消息。发布者向订阅者发送订阅令牌(Subscription)。使用订阅令牌,订阅者可以从发布者那里请求多个消息。当消息元素准备就绪时,发布者向订阅者发送多个或更少的元素。然后订阅者可以再次请求更多的消息元素,或取消订阅。一个发布者可能需要处理来自多个订阅者的请求。
Subscription(订阅费),订阅令牌。当订阅请求成功时,发布者将其传递给订阅者。订阅者使用订阅令牌与发布者进行交互,例如请求更多的消息元素或取消订阅。
Processor,即处理器,充当订阅者和发布者的处理阶段。Processor 接口继承了 Publisher和 Subscriber 接口。它用于转换发布者/订阅者管道中的元素。Processor<T, R>会将来自于发布者的 T 类型的消息数据,接收并转换为 R 类型的数据,并将转换后的 R 类型数据发布给订阅者。一个发布者可以拥有多个处理者
JDK中带的发布者类java.util.concurrent.SubmissionPublisher
SubmissionPublisher能够以反应流的方式生成元素(消息),并以异步方式发布元素(消息)。
* 代码示例:
1.处理器类
2. 订阅者
3. 测试类
* 参考文章:
反应式流 Reactive Streams 入门介绍 - 知乎
反应式流 - Reactive Stream丶Java教程网-IT开发者们的技术天堂
https://www.csdn.net/tags/NtzaYgysNzY3Mi1ibG9n.html
[webflux与mvc](Reactor-Netty与Spring WebFlux解读 之 第一章 为什么需要Spring WebFlux - 知乎)
// todo