五、流式编程

流是什么

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));
    }
原文地址:https://www.cnblogs.com/bigfairy/p/14002071.html