• 再来看看Java的新特性——Stream流


    半年前开始试着使用Java的新特性,给我印象最深的就是Stream流和Optional。其中Stream提高了看法效率,让代码看起来十分清爽。

    为什么要使用流?

    摘要中已经说明了,为了提高开发效率。流可以帮助我们高效操作集合,流帮助我们通过流水线的方式对集合进行删减合并排序修改,并最终返回我们想要的元素数据或统计数据。流水线的意思是说,一批元素不需要等待全部元素都完成某步操作,才进行下步操作,而是可以尽早进行下步操作,就好像流水线工厂一样,这为流高效运作提供了基础。流还有一个内部迭代的概念,就是把for循环显示迭代隐藏起来了,这样可以更方便的并行开发

    流的使用

    准备

    在使用流之前需要做些准备,首先说明两个概念:中间操作终端操作中间操作就好比生产车间的一步一步元素处理,每步处理之后,里面的元素样式或者数据结构可能发生改变了,但是它还是流。终端操作就好比产品生产完了,要打包装箱,变成最终消费者可见的最终形态,此时,它已经是产品,不再是流了。

    接着为了演示操作,先模拟几条数据

    List<JSONObject> menu = new ArrayList<>();
    menu.add(new JSONObject().putOpt("name","宫保鸡丁").putOpt("price","28"));
    menu.add(new JSONObject().putOpt("name","鱼香肉丝").putOpt("price","30"));
    menu.add(new JSONObject().putOpt("name","肉夹馍").putOpt("price","6"));
    menu.add(new JSONObject().putOpt("name","煎饼").putOpt("price","6"));
    

    常用的中间操作

    filter

    filter应该是Stream操作里面最常见的了,过滤器顾名思义就是过滤数据用的,filter的参数可以是lambda表达式。

    //比如下面这句话,就是得到所有价格小于10的食物,得到的还是流。
    //stream()方法将集合转成流
    menu.stream().filter(jsonObject -> jsonObject.getInt("price")<10);
    

    distinct 、 limit 和 skip

    distinct是去重,limit是截取前几个元素,skip是跳过前多少个元素。

    List<Integer> integerList = new ArrayList<>();
    integerList.add(1);
    integerList.add(1);
    integerList.add(2);
    integerList.add(3);
    integerList.stream()
        .distinct()//经过去重,流还剩1、2、3
        .skip(1)//跳过第一个元素,流中还有2、3
        .limit(1);//截取第一个元素,流中还剩2
    

    map

    map映射,上面的filter是将元素筛选,map则是改变元素的样式。比如,我们想要知道所有小于10块食物的名字。

    menu.stream()
        .filter(jsonObject -> jsonObject.getInt("price")<10)//此时还是jsonObject
    	.map(jsonObject -> jsonObject.getStr("name"));//此时变成了String
    

    flatMap

    流的合并,可以将多个数组合并操作,这样返回元素不是流,而是具体元素本身了。

    Stream.of(menu,foreignMenu)//此时元素是流 List<Stream>
        .flatMap(x -> x.stream())//此时元素是jsonObject List<jsonObject>
        .map(jsonObject -> jsonObject.getStr("name"))
        .distinct();
    

    常用的终端方法

    allMatch、anyMatch、noneMatch、findFirst和findAny

    方法 含义
    allMatch 检查谓词是否匹配所有元素
    anyMatch 检查谓词是否至少匹配一个元素
    noneMatch 确保流中没有任何元素与给定的谓词匹配
    findFirst 查找第一个符合条件的元素
    findAny 将返回当前流中的任意元素
    //前三个方法都是返回boolean类型
    boolean allMatchBool = menu.stream()
        .allMatch(jsonObject -> jsonObject.getInt("price") < 10);
    boolean noneMatchBool = menu.stream()
        .noneMatch(jsonObject -> jsonObject.getInt("price") < 10);
    boolean anyMatchBool = menu.stream()
        .anyMatch(jsonObject -> jsonObject.getInt("price") < 10);
    

    上面个方法返回的都是boolean类型,findFirst、findAny返回的都是元素。

    //关于Optional,先不关心,总之是元素就对了
    Optional<JSONObject> first = menu.stream().findFirst();
    Optional<JSONObject> any = menu.stream().findAny();
    
    System.out.println(first.get().toString());
    System.out.println(any.get().toString());
    
    //输出
    //{"price":"28","name":"宫保鸡丁"}
    //{"price":"28","name":"宫保鸡丁"}
    

    以上两个方法,只要找到符合条件的数据,流就提前结束了。为什么都是输出第一个元素,却要实现有两个方法呢?因为并行,findAny在并行方面限制会少一些。

    reduce

    最开始的时候说了,最终的返回值可以是元素集合,也可以是统计数据(或者说归纳),比如说元素求和。假设我们需要menu中所有食品各要一份需要花多少钱。

    Optional<Integer> price = menu.stream()//List<JsonObject>
        .map(jsonObject -> jsonObject.getInt("price"))//先将元素转成数字List<Integer>
        .reduce((x, y) -> x + y);
    System.out.println(price.get());
    

    max和min

    这个好理解,就是最大值和最小值嘛。效果类似于

    .reduce(Integer::max)
    .reduce(Integer::min)
    

    常用流汇总

    其中没有展示sorted、count这个都好理解。至于collect这个后面讲,用的比较多。

    流的转化

    除了对象流(Stream)以外,还有一些类型流,比如说 IntStream(以 IntStream 举例,其他类似)上面求和返回的是Optional对象,那可以直接返回Integer类型吗?

    //使用映射方法mapToInt()就ok了
    int price = menu.stream()//Stream
        .mapToInt(jsonObject -> jsonObject.getInt("price"))//IntStream
        .sum();
    //类型流转化回对象流,可以使用boxed()
    IntStream intStream = menu.stream()
    	.mapToInt(jsonObject -> jsonObject.getInt("price"));
    Stream<Integer> boxed = intStream.boxed();
    //当然了IntStream中有很多int类型操作的方法,就不一一举例了,源码打开一看,见名知意
    

    收集器

    前面讲的常用的中间操作,返回值都是流,还有一些中断操作,返回值都是Optional或者数值。可别忘了Stream最开始的初衷是为了解决集合操作问题。最终转化成集合使用的中断操作collect,参数是接口 Collector,里面有众多转化方法。

    转换成集合

    最常用的莫非toList() 这个方法了,将返回结果变成List。

    List<JSONObject> list = menu.stream()
        .filter(jsonObject -> jsonObject.getInt("price") < 10)
        .collect(Collectors.toList());
    //当然还有toSet()等等,触类旁通
    

    字符串拼接

    比较常用,就是字符串链接了。使用joining()方法

    String s = menu.stream()
        .filter(jsonObject -> jsonObject.getInt("price") < 10)
        .map(jsonObject -> jsonObject.getStr("name"))
        .collect(Collectors.joining(","));
    

    分组

    根据提供的属性分组,使用 groupingBy() ,为了方便说明,给上面各种食品一个type值:

    List<JSONObject> menu = new ArrayList<>();
    menu.add(new JSONObject().putOpt("name","宫保鸡丁").putOpt("price","28").putOpt("type","good"));
    menu.add(new JSONObject().putOpt("name","鱼香肉丝").putOpt("price","30").putOpt("type","good"));
    menu.add(new JSONObject().putOpt("name","肉夹馍").putOpt("price","6").putOpt("type","normal"));
    menu.add(new JSONObject().putOpt("name","煎饼").putOpt("price","6").putOpt("type","normal"));
    
    Map<String, List<JSONObject>> type = menu.stream()
        .collect(Collectors.groupingBy(jsonObject -> jsonObject.getStr("type")));
    System.out.println(type);
    //输出
    //{normal=[{"price":"6","name":"肉夹馍","type":"normal"}, {"price":"6","name":"煎饼","type":"normal"}], good=[{"price":"28","name":"宫保鸡丁","type":"good"}, {"price":"30","name":"鱼香肉丝","type":"good"}]}
    

    与分组类似的还有一个方法 partitioningBy (),分区,不过它的参数位于是boolean类型。

  • 相关阅读:
    C#编程读写文本
    机械手臂四轴
    机械手臂姿态
    Val编程任务编程
    Val编程系统架构
    GeoGebra一种开源平面画图软件的使用
    Val编程速度因子
    Val编程val系列编程思想
    怎么让 dom4j 添加的节点 在最前面 而不是最后面
    LayoutInflater的inflate函数用法详解
  • 原文地址:https://www.cnblogs.com/pjjlt/p/11874127.html
Copyright © 2020-2023  润新知