五、流式编程

流是什么

Stream(流)是一个来自数据源元素队列并支持聚合操作

  • 元素队列:特定类型的对象形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源:流的来源。可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作:类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

流的组成

+--------------------+ +------+ +------+ +----+ +--------+ | stream of elements |-----> |filter|-> |sorted|-> |map |-> |collect | +--------------------+ +------+ +------+ +----+ +--------+ | 数据源 |-----> | 中间操作 |-> |终端操作 | +--------------------+ +------+ +------+ +----+ +--------+

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

流操作分类

  1. 中间操作(intermediate operation)
    • 无状态操作
      • 过滤(filter)
      • 映射(map)
      • 扁平化(flatMap)
      • 遍历(peek)
    • 有状态操作
      • 去重(distinct)
      • 跳过(slip)
      • 截断(limit)
      • 排序(sorted)
  2. 终端操作(terminal operation)
    • 非短路操作
      • 遍历(forEach)
      • 归约(reduce)
      • 最大值(max)
      • 最小值(min)
      • 聚合(collect)
      • 计数(count等)
    • 短路操作
      • 所有匹配(allMatch)
      • 任意匹配(anyMatch)
      • 不匹配(noMatch)
      • 查找首个(findFirst)
      • 查找任意(findAny)

实例

/** * @author fangliu * @date 2020-02-13 * @description 演示流的各种操作 */ public class StreamOperatorTest { /** * 模拟测试数据 */ private static List<Goods> goodsList= new ArrayList<Goods>(){ { add(new Goods(111,"无人机", GoodTypeEnum.DIGITAL,10000.00, 10000.00,1)); add(new Goods(112,"VR一体机", GoodTypeEnum.DIGITAL,13000.00, 13000.00,1)); add(new Goods(113,"衬衫", GoodTypeEnum.APPAREL,100.00, 300.00,3)); add(new Goods(114,"牛仔裤", GoodTypeEnum.APPAREL,120.00, 120.00,1)); add(new Goods(115,"Java编程思想", GoodTypeEnum.BOOKS,80.00, 80.00,1)); add(new Goods(116,"Java核心技术", GoodTypeEnum.BOOKS,90.00, 90.00,1)); add(new Goods(117,"算法", GoodTypeEnum.BOOKS,60.00, 60.00,1)); add(new Goods(118,"跑步机", GoodTypeEnum.SPORTS,3600.00, 3600.00,1)); } }; /** * forEach 来迭代流中的每个元素 */ @Test public void forEachTest(){ goodsList.stream() .forEach(goods->System.out.println(JSON.toJSONString(goods))); } /** * filter 通过设置的条件过滤出元素 */ @Test public void filterTest(){ goodsList.stream() // 过滤出商品中的图书类 .filter(goods-> GoodTypeEnum.BOOKS.equals(goods.getGoodType())) .forEach(goods->System.out.println(JSON.toJSONString(goods))); } /** * map 映射每个元素到对应的结果 */ @Test public void mapTest(){ goodsList.stream() // 将商品中名字映射到结果中 .map(goods-> goods.getGoodName()) .forEach(goods->System.out.println(JSON.toJSONString(goods,true))); } /** * flatMap 将一个对象转换成流 */ @Test public void flatMapTest(){ goodsList.stream() // 将商品中名字转换成流 .flatMap(goods-> Arrays.stream(goods.getGoodName().split(""))) .forEach(goods->System.out.println(JSON.toJSONString(goods,true))); } /** * peek 来迭代流中的每个元素,与forEach相似,但不会销毁流元素 */ @Test public void peekTest(){ goodsList.stream() // 迭代商品中的商品名字 .peek(goods-> System.out.println(goods.getGoodName())) .forEach(goods->System.out.println(JSON.toJSONString(goods,true))); } /** * sorted 对流中的元素进行排序 可选择自然排序或者排序规则 */ @Test public void sortedTest(){ goodsList.stream() // 按商品的价格进行排序 .sorted(Comparator.comparing(goods -> goods.getGoodPrice())) .forEach(goods->System.out.println(JSON.toJSONString(goods))); } /** * distinct 对流中的元素去重 */ @Test public void distinctTest(){ goodsList.stream() // 将商品类型映射到结果中 .map(goods ->goods.getGoodType()) //去重 .distinct() .forEach(goods->System.out.println(JSON.toJSONString(goods))); } /** * skip 跳过前N条元素 */ @Test public void skipTest(){ goodsList.stream() // 按商品的价格进行排序, .sorted(Comparator.comparing(goods -> goods.getGoodPrice())) // 跳过前两条 .skip(2) .forEach(goods->System.out.println(JSON.toJSONString(goods))); } /** * limit 截断前N条元素 */ @Test public void limitTest(){ goodsList.stream() // 按商品的价格进行排序, .sorted(Comparator.comparing(goods -> goods.getGoodPrice())) // 截断前两条 .limit(2) .forEach(goods->System.out.println(JSON.toJSONString(goods))); } /***********************************************/ /** *allMatch 必须全部都满足才会返回true */ @Test public void allMatchTest(){ boolean allMatch =goodsList.stream() .peek(goods->System.out.println(JSON.toJSONString(goods))) // 商品单价大于500 .allMatch(goods -> goods.getGoodPrice()>500); System.out.println(allMatch); } /** *anyMatch 只要有一个条件满足即返回true */ @Test public void anyMatchTest(){ boolean allMatch =goodsList.stream() .peek(goods->System.out.println(JSON.toJSONString(goods))) // 商品单价大于1000 .anyMatch(goods -> goods.getGoodPrice()>1000); System.out.println(allMatch); } /** *noneMatch 全都不满足才会返回true */ @Test public void noneMatchTest(){ boolean allMatch =goodsList.stream() .peek(goods->System.out.println(JSON.toJSONString(goods))) // 商品单价大于10000 .noneMatch(goods -> goods.getGoodPrice()>10000); System.out.println(allMatch); } /** *findFirst 找到第一个元素 */ @Test public void findFirstTest(){ Optional optional =goodsList.stream() .findFirst(); System.out.println(JSON.toJSONString(optional.get())); } /** *findAny 找到任意一个元素 */ @Test public void findAnyTest(){ for (int i = 0; i < 20; i++) { Optional optional =goodsList.stream() .findAny(); System.out.println(JSON.toJSONString(optional.get())); } } /** * mapToInt/mapToLong/mapToDouble 主要用于int、double、long等基本类型上,进行统计结果 */ @Test public void mapToXXTest(){ DoubleSummaryStatistics stats = goodsList.stream() // 将商品价格映射到流中 .map(goods ->goods.getGoodPrice()) .mapToDouble((x)-> x).summaryStatistics(); System.out.println("商品中价格最贵的商品 : " + stats.getMax()); System.out.println("商品中价格最便宜的商品 : " + stats.getMin()); System.out.println("所有商品的价格之和 : " + stats.getSum()); System.out.println("商品的平均数 : " + stats.getAverage()); } }

流的构建

  1. 由值创建流
  2. 由数组创建流
  3. 由文件创建流
  4. 由函数生产流

实例

/** * @author fangliu * @date 2020-02-14 * @description 流的四种构建形式 */ public class StreamConstructor { /** * 由值创建流 */ @Test public void streamFromValue(){ Stream stream = Stream.of(1, 2, 3, 4, 5, 6); stream.forEach(System.out::println); } /** * 由数组创建流 */ @Test public void streamFromArrays(){ int[] numbers = {1, 2, 3, 4, 5, 6}; IntStream stream = Arrays.stream(numbers); stream.forEach(System.out::println); } /** * 由文件创建流 */ @Test public void streamFromFiles() throws IOException { String path = "/Users/fangliu/data/workspace/study/src/test/java/com/example/demo/stream/StreamConstructor.java"; Stream stream = Files.lines(Paths.get(path)); stream.forEach(System.out::println); } /** * 由函数生产流(无限流) */ @Test public void streamFromValue1(){ //Stream stream = Stream.iterate(0,n->n+2); Stream stream = Stream.generate(Math::random); stream.limit(100).forEach(System.out::println); } }

收集器

  • 将流中的元素累积成一个结果
  • 作用于终端操作的collect()上
  • collect/Collector/Collectors

预定义收集器功能

  • 将流元素归约
  • 将流元素分组
  • 将流元素分区
/** * collect Collectors类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串 */ @Test public void collectTest(){ // 将商品价格大于1500的转换成商品集合 List<Goods> list=goodsList.stream() .filter(goods -> goods.getGoodPrice()>1500) .collect(Collectors.toList()); System.out.println(list); //将商品名用逗号拼接成字符串 String str=goodsList.stream() .map(goods -> goods.getGoodName()) .collect(Collectors.joining(",")); System.out.println(str); // Map<分组条件,结果集合> Map<Object,List<Goods>> group=goodsList.stream() //按照商品类型分组 .collect(Collectors.groupingBy(goods -> goods.getGoodType())); System.out.println(JSON.toJSONString(group,true)); // Map<分区条件,结果集合> 分区是分组的一个特例 Map<Boolean,List<Goods>> partition=goodsList.stream() //商品大于1000的商品归为true 其余false .collect(Collectors.partitioningBy(goods -> goods.getGoodPrice()>1000)); System.out.println(JSON.toJSONString(partition,true)); }

__EOF__

本文作者柳小白
本文链接https://www.cnblogs.com/bigfairy/p/14002071.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   柳小白  阅读(431)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示