Reactor学习:五、中间操作—转换

声明:差不多就是Transforming an Existing Sequence的翻译


一、转换当前的序列
  • 一对一转换(比如将当前字符串的值转换为其长度):map
    Flux.just("str1" , "str").map(item -> {return   item.length();}).subscribe(System.out::println);//4 3 
    
    • 仅作类型转换:cast
      Mono.just("str1").cast(Object.class);
      
    • 将元素转换为带序号的封装元素,序号来自index操作所接收到的顺序,从0开始
      Flux.just("str1","str2").index();//3.1.0版本似乎没有这个方法
      
    • 将元素转换为带有记录间隔时间的封装元素,间隔时间是elapsed操作接收到上一个数据到当前数据的时间间隔
      Flux.interval(Duration.ofSeconds(1)).elapsed().subscribe(System.out::println,System.out::println,null,sub -> sub.request(3));
      //-----------------------
      [1007,0]
      [1001,1]
      [999,2]
      
  • 一对多转换(比如将字符串转换为组成它们的字符): flatMap
    注意:flatMap会将其内部多个Publisher合成为一个(按照时间顺序),所以最后展现的还是一个序列
    Flux.just("str1","str2").flatMap(item -> {
                return Flux.fromStream(item.chars().mapToObj(c ->   Character.valueOf((char) c)));
            } , 2).subscribe(System.out::println);
    //----------------(结果去掉换行)
    s t r 1 s t r 2
    
    • 如果想忽略掉flatMap中某些数据可以使用Mono.empty()
    • 如果想使得flatMap的数据按原数据顺序排列(比如转换公式为:str => s t r ,原数据如果为str1 str2 那么转换后的排序顺序必为 s t r 1 s t r 2,即只要上一个元素内部转换流没有走完,下一个转换内容的永远不会被输出,但会被记录),可以试试Flux#flatMapSequential
    • Mono的一对多转换(转换为Flux):Mono#flatMapMany

二、在已有序列中增添新的元素
  • 在序列之前 :Flux#startWith
    Flux.just(1,2,3).startWith(0).subscribe(System.out::println);
    //------------------
    0 1 2 3
    
  • 在序列之后 :Flux#concatWith

三、对Flux进行归并
  • 归并为List:Flux#collectList , Flux#collectSortedList
    等待接收完成,并将所有数据归并成一个Mono<List>进行返回,后者比前者多个排序功能

    Flux.just(3,5,2,1).collectSortedList((left , right) -> {
        if(left > right){
            return 1;
        }else if(left < right){
            return -1;
        }else{
            return 0;
        }
    }).subscribe(System.out::println);
    //---------------
    [1, 2, 3, 5]
    
  • 归并为Map:Flux#collectMap , Flux#collectMultiMap
    等待接收完成,将所有数据归并为一个Mono<Map>进行返回,后者返回的是Mono<Map<key,Collection>>

    Flux.just(3,5,2,1).collectMap(item -> item.toString()).subscribe(System.out::println);
    //------------------------
    //{1=1, 2=2, 3=3, 5=5}
    
  • 由Collector来完成归并:Flux#collect

    Flux.just(3,5,2,1).collect(Collectors.counting()).subscribe(System.out::println);
    //--------------------
    //4
    
  • 计算序列中元素数量:Flux#count

    Flux.just(3,5,2,1).count().subscribe(System.out::println);
    //-----------------------------------
    //4
    
  • 对序列中的每一个元素应用一个回调,该回调的结果会带入下一次回调,直到所有元素被转换为一个最终回调结果:Flux#reduce

    Flux.just(3,5,2,1).reduce("number:" , (lastResult,item) -> lastResult + item.toString()).subscribe(System.out::println);
    //----------------
    //number:3521
    
    • 在Flux#reduce的基础上,增加输出每次转换结果的功能:Flux#scan
      Flux.just(3,5,2,1).scan("number:" , (lastResult,item) -> lastResult + item.toString()).subscribe(System.out::println);
      //-------------------------
      number:
      number:3
      number:35
      number:352
      number:3521
      
  • 归并成布尔值

    • 指定判断式,是否符合所有元素:Flux#all
    Flux.just(3,5,2,1).all(item -> item < 10).subscribe(System.out::println);
    //---------------
    //true
    

    注意,如果有一个不符合,将会立刻向上游发送cancel信号,并向下游发出元素false

    • 指定判断式,是否至少有一个符合:Flux#any

    注意,如果有一个符合,将会将会立刻向上游发送cancel信号,并向下游发出元素true,下面两个也同理,达到要求便立刻中止

    • 检验序列中是否有元素:Flux#hasElements()
      Flux.empty().hasElements().subscribe(System.out::println);
      //---------------
      //false
      
    • 检验序列中是否有特定元素:Flux#hasElement(T value)

四、合并多个生产者
  • 以序列的顺序进行合并:Flux#concatWith(other)
    Flux.just(1,2,3).concatWith(Flux.just(4,5,6)).subscribe(System.out::println);
    //---------------------------------
    //1 2 3 4 5 6
    
  • 以发出的元素顺序进行合并,先发出的元素在前:Flux#mergeWith(other)
    Flux.interval(Duration.ofSeconds(1)).mergeWith(Flux.interval(Duration.ofSeconds(1))).subscribe(System.out::println);
    //---------------
    //0 0 1 1 2 2 3 3 ....
    
    • mergeWith基础上,增加了兼容不同类型生产者的功能和合并元素的功能,如[1,2,3]和[a,b,c,d]会合并成[1a,2b,3c]:Flux#zipWith
      Flux.just(1,2,3).zipWith(Flux.just("a" , "b" , "c")).subscribe(System.out::println);
      //------
      //[1,a] [2,b] [3,c]
      Flux.interval(Duration.ofSeconds(1)).zipWith(Flux.just("a","b","c") , (f,s) -> f.toString().concat(s)).subscribe(System.out::println);
      //--------
      //0a 1b 2c
      
  • 等待另一个序列结束,然后丢出一个Mono<Void>:Mono#and
    Mono.just("e").and(Flux.interval(Duration.ofSeconds(1)).take(3).doOnNext(System.out::println)).subscribe(null , null ,() -> System.out.println("ok"));
    //-----------
    //0 1 2 ok
    
  • 等待指定所有序列结束,然后丢出一个Mono<Void>:Mono#when
    Mono.when(Flux.just(1,2,3) , Flux.interval(Duration.ofSeconds(1)).take(3).doOnNext(System.out::println)).subscribe(null , null ,() -> System.out.println("ok"));
    //-------------
    //0 1 2 ok
    
  • zipWith相似,但是每次合并的元素都取自其他序列发出的最近一个值,而不是一直等待其他序列发出下一个值:Flux#combineLatest
    Flux.combineLatest(objects -> objects[0].toString().concat(objects[1].toString()) ,Flux.interval(Duration.ofSeconds(1)).take(3) , Flux.just("a","b","c")).subscribe(System.out::println);
    // 0c 1c 2c
    
  • 有一个生产者序列集合,以谁先发出第一个元素来判断选择哪个序列进行输出
    Flux.first(Flux.interval(Duration.ofSeconds(1)).take(3) , Flux.just(4,5,6)).subscribe(System.out::println);
    //----------
    // 4 5 6
    
  • flatMap相似,但是当转换的序列还没结束,但是主序列的下一个元素已经到来额时候,会直接取消掉转换后的序列,也就是说同时只能存在一个转换的序列被执行:switchMap

五、重复
  • 完成后,重新订阅该序列,重复输出,永不停止:repeat
    • 上述功能,再加上时间间隔:Flux.interval(duration).flatMap(tick → myExistingPublisher)

六、只对完成信号感兴趣,即忽略元素
  • 忽略所有的元素,如果上游发出完成信号,则完成:ignoreElements
  • 忽略所有的元素,只响应错误信号和完成信号,完成返回Mono<Void> : then
    • 变体--响应完成时不再返回Mono<Void>,而是正常执行参数中的Mono,并将其返回值作为返回值:then(Mono<T> other)
  • 完成后,还要再完成一个提供的空任务后才返回: Mono<Void> thenEmpty(Publisher<Void> other)
  • 完成后,返回提供的值:Mono<T> Mono#thenReturn(T)
  • 完成后,执行提供的Flux,其元素会正常输出:thenMany

七、有一个需要延迟完成的Mono
  • 需要等待该Mono中元素所生成的序列完成后再向下游发出该元素:Mono#delayUntil(Function)
    Mono.just("complete!").delayUntil(item -> Flux.interval(Duration.ofSeconds(1)).take(3).doOnNext(System.out::println)).subscribe(System.out::println);
    //-------------------------------------------
    //0 1 2 complete!
    

八、如果需要对流中元素进行递归操作
  • 以分支为先展开: Flux#expand(Function)
    即先对所有元素进行一遍递归,然后再对各个递归后序列中的序列进行递归
    Flux.just(1,2).expand(item -> item > 6 ? Mono.empty() : Flux.just(item *2)).subscribe(System.out::println);
    //--------------
    //1 2 2 4 4 8 8
    
  • 以深度为先展开:Flux#expandDeep(Function)
    Flux.just(1,2).expandDeep(item -> item > 6 ? Mono.empty() : Flux.just(item *2)).subscribe(System.out::println);
    //-----------------
    //1 2 4 8 2 4 8
    

九、序列为空时转换
  • 如果序列为空,则输出提供的预定值: defaultIfEmpty
  • 如果序列为空,则订阅提供的预定序列::switchIfEmpty

参考文档:
[1] Reactor api doc
[2] Reactor reference doc

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270