范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

WebFlux前置知识(三)

  Stream 流式 API 也是 JDK8 开始引入的 API,用过的小伙伴都说爽,没用过的小伙伴则感到一脸懵逼。现在 JDK 已经到 16 了,可能很多人还不熟悉 Stream 流操作,这次趁着讲 WebFlux,和大家一起回顾一下 Stream 流操作。  1.Stream 流简介
  说到 Stream,大家可能很容易想到 Java IO 中的各种流操作,名字看起来很像,但这其实是两个完全不同的东西。
  Java8 中的 Stream 不存储数据,它通过函数式编程模式来对集合进行链状流式操作。  2.基本玩法
  先来个简单的例子感受下 Stream 的操作。
  例如我有一个数组,如下:  int[] arr = {1, 2, 3, 4, 5, 6};
  现在想给这个数组求平均数,如下:  double asDouble = IntStream.of(arr).average().getAsDouble();
  一行代码搞定,首先调用 IntStream.of 方法获取流对象,然后调用 average 方法计算平均数,再调用 getAsDouble 方法获取结果。全程都是方法调用,不用自己写求平均数的逻辑。
  再比如求和,如下:  int sum = IntStream.of(arr).sum();
  也是直接调用 sum 方法即可。
  而且我们还可以在流中对数据进行二次加工,例如给数组中的每个元素先求平方再求和:  double[] arr = {1, 2, 3, 4, 5, 6}; double sum = DoubleStream.of(arr).map(i -> Math.pow(i, 2)).sum();
  先在 Map 中对每一个元素求平方,然后再求和。
  这里就能涉及到两个概念:  中间操作  终止操作
  所谓中间操作,就是中途求平方的操作,所谓终止操作就是最终计算出结果的操作,只要方法的返回值不是一个 Stream,那就是终止操作,否则就是中间操作。  3.Stream 的创建
  获取一个流的方式多种多样,最常见的就是从集合或者数组中获取一个流,当然我们也可以直接自己创建一个流出来。
  一起来看下。  集合
  List 集合或者 Set 集合都可以直接搞一个流出来,方式如下:  List list = new ArrayList<>(); Stream s1 = list.stream(); Set set = new HashSet<>(); Stream s2 = set.stream();
  集合中直接调用 stream 方法就可以获取到流。  数组
  通过数组获取流的方式也很简单,如下:  IntStream stream = Arrays.stream(new int[]{11, 22, 33, 44, 55, 66}); 数字 Stream
  也可以直接利用 IntStream、LongStream 等对象创建一个数字 Stream,如下:  IntStream s1 = IntStream.of(1, 2, 3); DoubleStream s2 = DoubleStream.of(1, 2, 3); LongStream s3 = LongStream.of(1L, 2L, 3L); 自己创建Random random = new Random(); Supplier supplier = () -> random.nextInt(100); Stream stream = Stream.generate(supplier).limit(5);
  调用 Stream.generate 方法可以自己创建一个流,自己创建的时候需要提供一个 Supplier,通过调用 Supplier 中的 get 方法自动获取到元素。
  无论哪种创建方式,大家需要明白的是,Stream 并不会保存数据,它只会对数据进行加工。  4.Stream 的中间操作
  中间操作可以分为两大类:  map 或者 filter 会从输入流中获取每一个元素,并且在输出流中得到一个结果,这些操作没有内部状态,称为无状态操作。  reduce、sum、max 这些操作都需要内部状态来累计计算结果,所以称为有状态操作。
  分别来看下:
  无状态操作  map/mapToXxx  flatMap/flatMapToXxx  filter  peek
  有状态操作  distinct  sorted  limit/skip
  map
  Stream.map() 是 Stream 中最常用的一个转换方法,可以把一个 Stream 对象转为另外一个 Stream 对象。map 方法所接收的参数就是一个 Function 对象,松哥在前面文章中和大家介绍过 Function 对象了,就是有输入有输出(参见WebFlux 前置知识(一)),了解了 map 的参数,那么 map 的功能就很明白了,就是对数据进行二次加工。
  举个栗子,例如把一个字符串数组转为数字:  String[] arr = {"1", "2", "3"}; Stream s1 = Arrays.stream(arr); Stream s2 = s1.map(i -> Integer.valueOf(i));
  再比如一个数字流,给所有的元素乘 2,如下:  IntStream.of(1, 2, 3).map(i -> 2 * i).forEach(System.out::println);
  最后的 forEach 就是将元素打印出来。
  JDK 中也提供了一些现成的格式转换,如下图:
  这样可以直接将元素转为 Double、Long、Obj 等类型,例如下面这样:  String[] arr = {"1", "2", "3"}; Stream s1 = Arrays.stream(arr); s1.mapToLong(i -> Long.parseLong(i)).forEach(System.out::println);
  flatMap
  flatMap 可以把 Stream 中的每个元素都映射为一个 Stream,然后再把这多个 Stream 合并为一个 Stream。
  例如如下代码,返回的 Stream 中的元素是数组:  Stream s = Stream.of(new Integer[]{1, 2, 3}, new Integer[]{4, 5, 6}, new Integer[]{7, 8, 9}); s.forEach(System.out::println);
  通过 flatMap 我们可以将 Stream 中的元素变为 Integer:  Stream s = Stream.of(new Integer[]{1, 2, 3}, new Integer[]{4, 5, 6}, new Integer[]{7, 8, 9}).flatMap( i -> Arrays.stream(i)); s.forEach(System.out::println);
  filter
  filter 操作会对一个 Stream 中的所有元素一一进行判断,不满足条件的就被过滤掉了,剩下的满足条件的元素就构成了一个新的 Stream。
  例如要找到数组中所有大于 3 的元素,如下:  IntStream.of(2, 3, 4, 5, 6, 7).filter(i -> i > 3).forEach(System.out::println);
  filter 方法接收的参数是 Predicate 接口函数,关于 Predicate 接口函数,大家可以参考WebFlux 前置知识(一))一文。
  peek
  peek 的入参是 Consumer,没有返回值,因此当我们要对元素内部进行处理时,使用 peek 是比较合适的,这个时候可以不用 map(map 的入参是 Function,它是有返回值的)。peek 方法本身会继续返回流,可以对数据继续进行处理。
  举个简单的数据转换的例子吧(最终返回的数据并不会被转换):  IntStream.of(2, 3, 4, 5, 6, 7).filter(i -> i > 3).peek(String::valueOf).forEach(i-> System.out.println(i)); ❝
  peek 方法的感觉就像数据中途被消费了一次。
  distinct
  这个是去重。由于去重操作需要获取到其他元素的值(比较之后才知道是否重复),所以这个是有状态操作。如下:  IntStream.of(2, 3, 4, 3, 7, 6, 2, 5, 6, 7).distinct().forEach(System.out::println);
  sorted
  sorted 是排序,因为也需要知道其他元素的值,然后才能去重,所以这个也是有状态操作,如下:  IntStream.of(2, 3, 4, 3, 7, 6, 2, 5, 6, 7).distinct().sorted().forEach(System.out::println);
  limit/skip
  limit 和 skip 配合操作有点像数据库中的分页,skip 表示跳过 n 个元素,limit 表示取出 n 个元素。例如下面这个例子:  Arrays.asList("A", "B", "C", "D", "E", "F").stream().skip(2).limit(3).forEach(System.out::println);
  这个会跳过 A 和 B,最终打印出 C D E。这也是一种有状态操作。  5.Stream 终止操作
  终止操作就是最终计算出结果的操作,只要方法的返回值不是一个 Stream,那就是终止操作,否则就是中间操作。
  终止操作又分为两类:  短路操作:不用处理全部元素就可以返回结果。  非短路操作:必须处理所有元素才能得到最终结果。
  各自都包含哪些操作,我们分别来看下:
  非短路操作  forEach/forEachOrdered  collect/toArray  reduce  min/max/count
  短路操作  findFirst/findAny  allMatch/anyMatch/noneMatch
  forEach/forEachOrdered
  forEach 和 forEachOrdered 都是接收一个 Consumer 类型的参数,完成对参数的消费,不同的是,在并行流中,forEachOrdered 会保证执行顺序。
  例如如下一段代码:  int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; Arrays.stream(arr).parallel().forEach(System.out::println); Arrays.stream(arr).parallel().forEachOrdered(System.out::println);
  前者打印出来的顺序不一定是 123456789,后者一定是。
  collect/toArray
  这两个都是收集器,可以将执行结果转为一个 List 集合或者一个数组:  List list = Stream.of(1, 2, 3, 4).filter(p -> p > 2).collect(Collectors.toList()); System.out.println(list);
  reduce
  reduce 是 Stream 的一个聚合方法,它可以把一个 Stream 的所有元素按照聚合函数聚合成一个结果。reduce 方法传入的对象是BinaryOperator 接口,它定义了一个apply 方法,负责把上次累加的结果和本次的元素进行运算,并返回累加的结果。
  举个简单的例子,数组求和,当然可以直接调用 sum 计算,我们这里也可以调用 reduce 来实现,如下:  Optional optional = Stream.of(1, 2, 3, 4).reduce((i, j) -> i + j); System.out.println(optional.orElse(-1));
  reduce 的参数是 BinaryOperator,这个接收两个参数,第一个参数是之前计算的结果,第二个参数是本次参与计算的元素,两者累加求和。
  再比如给一段话中间加上.,方式如下:  Optional s = Stream.of("wwwjavaboyorg".split("")).reduce((i, j) -> i + "." + j); System.out.println(s.orElse(""));
  最终执行结果如下:  w.w.w.j.a.v.a.b.o.y.o.r.g
  min/max/count
  这个就比较简单了,就是求最大值最小值,统计总个数,如下表示统计总个数:  Stream s = Stream.of(1, 2, 3, 4); long count = s.count(); System.out.println("count = " + count);
  如下表示统计最小值:  Stream s = Stream.of(1, 2, 3, 4); Optional min = s.min(Comparator.comparingInt(i -> i)); System.out.println("min.get() = " + min.get());
  findFirst/findAny
  这两个就是返回流中的第一个、任意一个元素,findAny 要在并行流中测试才有效果,举个栗子:  for (int i = 0; i < 10; i++) {     Optional first = Stream.of(1, 2, 3, 4).parallel().findFirst();     System.out.println("first.get() = " + first.get()); } System.out.println("============="); for (int i = 0; i < 10; i++) {     Optional first = Stream.of(1, 2, 3, 4).parallel().findAny();     System.out.println("first.get() = " + first.get()); }
  allMatch/anyMatch/noneMatch
  allMatch、anyMatch、noneMatch 用来判断所有元素、任意元素或者没有元素满足给定的条件。这三个方法的参数都是一个 Predicate 接口函数。  boolean b = Stream.of(1, 2, 3, 4).allMatch(i -> i > 5); System.out.println("b = " + b); 6.并行流
  通常情况下,对 Stream 的元素进行处理是单线程的,即一个一个元素进行处理。有时候我们希望可以并行处理 Stream 元素,因为在元素数量非常大的情况,并行处理可以大大加快处理速度。
  把一个普通 Stream 转换为可以并行处理的 Stream 非常简单,只需要用 parallel 方法进行转换:  new Random().ints().limit(50).parallel().forEach(i->{     System.out.println(Thread.currentThread().getName() + "--->" + i); });
  这样数据在后台就是并行打印的。  7.收集器
  收集器可以将计算结果重新整理收集到一个集合中,这个集合可以是一个 List/Set 获取其他,并且还可以在收集的过程中对数据进行处理。
  例如我有一个 users 集合,里边保存了用户数据,用户有 username、age 以及 gender 三个属性,如下代码分别表示:  提取出用户对象中的 age 属性组成新的集合并返回。  提取出用户对象中的 username 属性组成新的集合并返回。  提取出用户对象中的 gender 属性组成新的集合并返回(这里是一个 Set 集合,所以会自动去重)。  List ages = users.stream().map(User::getAge).collect(Collectors.toList()); System.out.println("ages = " + ages); List usernames = users.stream().map(User::getUsername).collect(Collectors.toList()); System.out.println("usernames = " + usernames); Set genders = users.stream().map(User::getGender).collect(Collectors.toSet()); System.out.println("genders = " + genders);
  Collectors.toList()  最终返回的是 ArrayList,Collectors.toSet()  最终返回的是 HashSet。
  如果我们想返回一个 Vector 或者 TreeSet,也是可以的,如下:  List ages = users.stream().map(User::getAge).collect(Collectors.toList()); System.out.println("ages = " + ages); List usernames = users.stream().map(User::getUsername).collect(Collectors.toCollection(Vector::new)); System.out.println("usernames = " + usernames); TreeSet genders = users.stream().map(User::getGender).collect(Collectors.toCollection(TreeSet::new)); System.out.println("genders = " + genders);
  也可以获取某一个字段的统计信息:  IntSummaryStatistics ageStatistics = users.stream().collect(Collectors.summarizingInt(User::getAge)); System.out.println("ageStatistics = " + ageStatistics);
  这个统计信息中包含:总和、最小值、平均值以及最大值等:  ageStatistics = IntSummaryStatistics{count=20, sum=1222, min=9, average=61.100000, max=96}
  还可以对数据进行分块,将男女不同性别统计出来:  Map> map = users.stream().collect(Collectors.partitioningBy(u -> u.getGender().equals("男"))); System.out.println("map = " + map);
  也可以按照性别对数据进行分组,如下:  Map> map2 = users.stream().collect(Collectors.groupingBy(User::getGender)); System.out.println("map2 = " + map2);
  分组后,Map 中的 key 就是性别;分块后,Map 中的 key 就是 true/false。
  再比如统计男女的人数:  Map map2 = users.stream().collect(Collectors.groupingBy(User::getGender,Collectors.counting())); System.out.println("map2 = " + map2);
  好啦,今天我们就先聊这么多~
  原文链接:https://mp.weixin.qq.com/s/cuxnkcfAOLZuxWRmYcnKWg

球王会,卡里索离队,最高兴的竟然是武汉队球迷据悉,武汉队外援卡里索与中超的合约将在11月份到期,而中超第二阶段的比赛在12月份才正式进行。截止到现在,卡里索并没有向武汉队提出续约,而是有意去西班牙球队阿尔梅里亚,将重回西班牙球王会LPL季后赛,小凤凰轻松拿下WE,微笑直播破防目前,FPX已经确定参加世界赛。小凤凰现在就是非常凶猛的状态。用网友的话来说,现在的FPX是LPL最强队。Smile也在直播间解说比赛,但结束后,其他球队的球迷都露出恶心的微笑。微球王会LpL季后赛首轮败者小组落幕,EDG成功击败LNGLpl季后赛首轮败者小组落幕,edg状态恢复得非常好,最终以31战胜lng进入下一轮的比赛。在比赛开始之前,很多人都更看好lng,因为如果edg输掉比赛的话,其他团队也有很多好处。球王会畅聊IG明年值得期待!前功勋教练即将回归LPL?IG粉丝在今年夏天的心情肯定非常不好,因为心爱的战队没能进入季后赛,成为今年唯一一支错过S赛的队伍。无论是老选手还是新选手,都或多或少存在问题。很多人甚至希望IG明年重组换血,否则球王会,S11比赛场地将离开LPL!从中国迁移至欧洲目前包括LPL在内的各个赛区的夏季赛都已经接近尾声,这就是说,S11全球总决赛就要来临了,截止目前,除了LPL没有确定晋级S11的队伍外,其他的几个赛区中,LEC和LCS的晋级队伍球王会LPL夏季赛FPX不负众望,成功击败WE前不久的一场比赛中,WE对战FPX,结果也令人不意外,毕竟两队的实力差距很大,最终由FPX拿下胜利。在第一局的比赛中,我们可以看到WE中野有点贪婪,在FPX中野已经返回城,并且补充球王会LPL夏季赛EDG轻取LNG,三比一拿下胜利2021年LPL夏季赛快要结束,最近的一场比赛是EDG对阵LNG。胜利的一方将继续为S赛门票而奋斗,而失败的一方将在夏季赛中锁定第四名。首局比赛,EDG全线发力,打得稳稳当当。在第球王会LPL季后赛即将结束,RNG已经开始准备冒泡赛如今,S11全球总决赛登陆欧洲的日子越来越近了。在新的LPL王者诞生之前,已经有很多职业LOL战队提前锁定了世界赛席位,比如来自LCK的T1DKGEN,以及LEC老牌豪门FNC等,球王会LPL战队FPX强势表现,现已锁定LPL首张S11门票LPL季后赛只剩下最后两场BO5比赛了。在FPX以30的战绩夺得WE之后,EDG也有机会与WE争夺总决赛的机会。作为胜者,FPX虽然决赛还没有开始,但凭借目前的高分,锁定了LPL首球王会LPL季后赛EDG强势晋级!水晶哥当场夸赞群雄哥对于大多数喜欢玩英雄联盟这款游戏的网友来说,一定对现在火爆进行中的LPL夏季赛不陌生。作为当今国内英雄联盟游戏圈最火爆的职业赛事,吸引了众多玩家的关注。同时,各大球队也在经过一段时球王会LPL各队阵容更新!JDG大名单没有LokenLPL夏季季后赛正在进行中。对于很多没有进入季后赛的球队来说,假期已经提前了,球队可以开始为明年的阵容做准备了。而就在最近,LPL官网更新了,有细心的网友发现JDG的阵容发生了变化
S12想再创辉煌?详细分析TES战队应该如何补强LPLS11夏季赛已经结束了,常规赛中TES最终取得了第5的好成绩。但是在季后赛第二轮被LNG31击败无缘世界赛。这只拿下过S10MSC季中杯冠军世界赛4强,被无数玩家期以厚望的T玩法收费被喷惨,逆水寒宣布第二流派永久免费大家还记得前段时间逆水寒宣布开放第二流派的事吗?当时官方称第二流派每个月需要88块钱,每个月可以通过武林威望和海盗积分来兑换3天切换第二流派的时间。听到第二流派收费后玩家们纷纷开喷三国杀OL想欺我祈福,痴心妄想!界贾诩惨遭削弱9月初,官方爆料了贾诩界限突破的初版技能,这突破可谓是诚意满满,强度非常imba。诸位杀友也纷纷表示就算是500灵宝也是值得,毕竟吊打潘叔卧龙凤雏和界魏延,美滋滋。初版界贾诩群3体NoStrike女装创世纪上架steam,玩家好评如潮9月18日,NoStrike女装三部曲最后一作女装创世纪上架steam,售价37元RMB,目前首发促销9折只卖33。30元RMB,女装系列完整合辑促销折扣42只需100。89元RM三国杀OL设计师江郎才尽?连抄4个新服武将9月15日,三国杀OL更新了新版本,这次更新带来了5个新武将,分别是花鬘吕旷吕翔辛毗丁原曹嵩。但是我万万没想到设计师居然如此没有节操,除吕旷吕翔以外的4个新武将居然是新服搬过来的,逆水寒跟着出哒哒哒猫咪跟宠,硬刚剑网三大家还记得前段时间剑网3出的跟宠哒哒哒吧,一只可爱的小猫咪,其实这个跟宠是为了嘲讽友商逆水寒。其实本来这波就是剑网3策划咸鱼为了转移玩家矛盾,表示近期游戏舆论危机都是别的友商挑拨离中秋节收割你的钱包!四大古装端游中秋外装哪个更好看?哈喽大家好,这里是小游民,马上也是中秋节了,大家吃了月饼柚子了吗?当然,作为一名网游玩家,我这边最关心的还是游戏里的中秋活动和中秋外观,那今天咱们也是来评判一下四大古装端游的中秋外咸鱼上台两年后,剑网3要被他搞黄了?2019年5月,剑网3宣布老策划咸鱼(余玉贤)回归。转眼已经过去两年,现在的剑网3相比2019年的剑网3是好是坏?咸鱼上台后到底给剑网3带来了怎么样的变化?讲这个问题之前,我要声明网易超激斗梦境测评,还是满满的猪厂味道9月17日,网易动作格斗网游超激斗梦境公测,虽然小游民我说过网易游戏狗都不玩,但是天天这么多广告砸向我,我也只能勉强进去试试(汪)。刚进游戏,我就感觉一股熟悉的味道扑面而来,仿佛我智力游戏华容道通关技巧目标玩家通过移动每个方块,但不可以拿起方块跨越棋子。只可以向平行或垂直的方向移动方块来帮助曹操从初始位置移到棋盘最下方中部的出口处逃走。方法1四个小兵(卒)必须两两在一起,不要轻易益智游戏华容道玩以消遣乐而忘忧益智游戏三国华容道是一款根据三国演义中关公在华容为感恩放走曹操的故事创造的滑块类游戏,根据不同的排兵布阵构成了开局不同的多关游戏。这个游戏有一个棋盘和十枚棋子组成兵是一个小正方形方