美文网首页
写给大忙人的javaSE8(3)-流操作使用总结

写给大忙人的javaSE8(3)-流操作使用总结

作者: 青山有扶苏 | 来源:发表于2019-03-03 22:31 被阅读0次
    学而不思则罔,思而不学则殆。
    

    温馨提示:这一接基本上是前面一节中的一些总结和使用注意事项,可以酌情跳过。

    前面一节中,说明了常用的Stream操作的部分常用API,本节中,将这些api用一个例子整合起来使用。
    1.api整合操作
    先假设提出一个需求:
    例如,我们现在有6个魔法师,他们来自不同的种族,不同的地区,不同的性别,甚至擅长各种各样的技能魔法。
    任务:在魔法师队伍中找到人类魔法师,并计算出他们来自哪些地区。

    按照以往的方式,在for循环中,根据race获取到魔法师是否为人类“homan”,并且并且将这些魔法师的来源地whence输出到一个Set,使用Set是因为,有可能两个或以上的魔法师来自同一个地区,但是我们这里的需求是,同一个地区不能出现两次。

    Set<String> sets = new HashSet<>();
    for(int i = 0; i < magicians.size();i++) {
      if ("human".equals(magicians.get(i).getRace())) {
        sets.add(magicians.get(i).getWhence())
      } 
    }
    

    可以这么理解。
    1.过滤出魔法师种族为人类的数据
    2.找到魔法师的来源地
    3.将魔法师来源放入 sets集合。

    接下来。我们将上面这段for循环改成 Stream方式,来练习一下使用Stream的API是如何完成上面这个任务的。

    //找出魔法师中 属于人类的魔法师
    Set<String> whences = magicians.stream()
            .filter(mg -> mg.getRace().equals("homan"))
            .map(mg -> mg.getWhence())
            .collect(Collectors.toSet());
    

    实际上,Stream操作更能直观的表达出我们的意图,首先,filter找出race为人类“homan”的魔法师,使用map操作获取魔法师的来源地 whence,然后将这个Stream使用提早取值的方法 collect转换成一个 Set。现在,是不是觉得Stream或许可以在你的代码中替代哪些复杂的操作了?

    2.重构以往的旧代码。

    以往复杂的代码,在采用流的方式重构,重构过程中,每一步都能确保通过单元测试,以保证代码能够正常工作。

    刚接触Stream时,可以先从for循环入手,
    1.首先使用Stream的forEach方法替代掉旧代码中的for循环。
    2.使用filter方式进行一些逻辑判定。
    3.使用map,flatmap等方式替换一些业务逻辑
    4.使用及早求值方法得到最终结果。

    3.多次调用流的操作。

    在前面的诸多例子中,我们大多时候都是用链式的方法调用Stream的api进行求值。但是你也可以每一步都强制采用及早求值法,不过不建议这么做。
    例如我们找魔法师的例子中

    //使用链式方法
    Set<String> whences = magicians.stream()
            .filter(mg -> mg.getRace().equals("homan"))
            .map(mg -> mg.getWhence())
            .collect(Collectors.toSet());
    
    //每一步强制对函数求值,而不是将所有方法调用连接一起
    //1.先过滤出种族为人类的魔法师
    List<Magician> magicianList = magicians.stream()
            .filter(mg -> mg.getRace()
            .equals("homan")).collect(Collectors.toList());
    
    //2.将魔法师的来源地放入map
    Set<String> whencesSet = magicianList.stream()
            .map(mg -> mg.getWhence()).collect(Collectors.toSet());
    

    毫无疑问,这种方也是可以达到目的的,不过该方法有以下几点区别。
    1.代码可读性差,样板代码过多,隐藏了真正的业务逻辑。
    2.效率差,每一步都要对流及早求值,生成新的集合。
    3.代码充斥着一堆垃圾变量,他们只是用来保存中间结果,除此之外,毫无用处。
    4.难于自动并行化处理。

    不过,刚开始接触流的方式时,可能会不知不觉中写出类似的代码,若有发现时应及时纠正,避免习惯的养成。

    4.正确使用Lambda表达式。
    使用lambda表达式时,要尽量避免产生副作用,这一点非常重要。没有副作用的函数不会改变程序或外接状态。在lambda表达式中向控制台输出信息或者给变量{不是lambda表达式内部的变量}赋值也是一种副作用,而且更难察觉, 无论何时,将lambda表达式传给Stream上的高阶函数,都应该尽量避免副作用。唯一例外的事forEach方法,因为它是一个终结方法。

    保持最好的心态,来迎接趣味人生
    

    相关文章

      网友评论

          本文标题:写给大忙人的javaSE8(3)-流操作使用总结

          本文链接:https://www.haomeiwen.com/subject/zgimuqtx.html