• 项目记事【StreamAPI】:使用 StreamAPI 简化对 Collection 的操作


    最近项目里有这么一段代码,我在做 code-review 的时候,觉得可以使用 Java8 StreamAPI 简化一下。

    这里先看一下代码(不是源码,一些敏感信息被我用其他类替代了):

        private static Set<String> doSomething1(String input) {
            Set<String> target = new HashSet<>();
            if (input != null) {
                for (Pojo pojo : source) {
                    if (input.equals(pojo.getStr1())) {
                        target.add(pojo.getStr2());
                    }
                }
            }
            return target;
        }

    其中 source 是一个 Set<Pojo> 的共享变量,Pojo 是自定义对象:

        private static class Pojo {
            String str1;
            String str2;
    
            Pojo(String str1, String str2) {
                this.str1 = str1;
                this.str2 = str2;
            }
    
            public String getStr1() {
                return str1;
            }
    
            public String getStr2() {
                return str2;
            }
        }

    这个 doSomething1() 方法的功能很简单:

    • 声明一个新的 Set<String> 结构 target。
    • 遍历 source。
    • 将 input 依次与 source 中每个元素的 str1 属性进行比对。
    • 若比对成功,将元素的 str2 属性加入 target。
    • 遍历结束,返回 target。

    从单纯的 Coding 角度而言,doSomething1() 方法实现得已经足够简便,但是使用 Java8 StreamAPI 可以仅仅使用一行代码的情况下完成这些操作。

    仔细分析以上的操作,可以归纳为三个步骤:

    • 过滤数据 -> input.equals(pojo.getStr1()
    • 提取数据 -> pojo.getStr2()
    • 收集数据 -> target.add(pojo.getStr2())

    这三个操作分别对应了 StreamAPI 的 filter,map,collect,因此可以简化成这样:

        private static Set<String> doSomething2(String input) {
            return source.stream().filter(pojo -> input.equals(pojo.getStr1())).map(Pojo::getStr2).collect(Collectors.toSet());
        }
    

    功能测试

        private static Set<Pojo> source = new HashSet<>();
    
        public static void main(String[] args) {
            prepareData();
            for (String s : doSomething1("1")) {
                System.out.println(s);
            }
            for (String s : doSomething2("1")) {
                System.out.println(s);
            }
        }
    
        private static void prepareData() {
            Pojo pojo1 = new Pojo("1", "2");
            Pojo pojo2 = new Pojo("3", "4");
            source.add(pojo1);
            source.add(pojo2);
        }

    输出如下:

    发现两者是等价的。

    性能测试

    一个应用程序的优秀与否,不在于代码的漂亮程度,而在于性能。

        public static void main(String[] args) {
            prepareData();
    
            long t3 = System.nanoTime();
            doSomething2("123441");
            long t4 = System.nanoTime();
            printTime(t3, t4);
    
            long t1 = System.nanoTime();
            doSomething1("123441");
            long t2 = System.nanoTime();
            printTime(t1, t2);
        }
    
        private static void printTime(long t1, long t2) {
            long t = t2 - t1;
            double factor = Math.pow(10, 9);
            System.out.println(t / factor);
        }
    
        private static void prepareData() {
            final int scale = 100000000;
            Random r = new Random();
            for (int i = 0; i < scale; ++i) {
                String str1 = r.nextInt(scale) + "";
                String str2 = r.nextInt(scale) + "";
                source.add(new Pojo(str1, str2));
            }
        }
    

    测试结果:

    可以发现,使用 StreamAPI 大大降低了程序的效率,当数据量足够大的时候,这个比例会不断缩小。

    我们可以这样理解:StreamAPI 针对的是相对大数据的操作,为一个数据量较小的 Collection 起一个 Stream 非常得不偿失,有种大炮打蚊子的感觉。

    最后的内存溢出,是在 prepareData 的时候,和处理数据没有关系。

    最后贴上大神的测试:Java8 Lambda表达式和流操作如何让你的代码变慢5倍

    所以流操作,慎用!

  • 相关阅读:
    C/C++ 编程中的内存屏障(Memory Barriers) (1)
    ubuntu 10.04 源
    内存屏障(经典)
    VMware设置桥接上网
    C/C++ 编程中的内存屏障(Memory Barriers) (2)
    寒假Day55:指针
    寒假Day54:poj2378Tree Cutting没用树形dp写的树的题dfs
    寒假Day50:CodeForces1324CFrog Jumps思维
    寒假Day50:51nod3047位移运算
    寒假Day53:Codeforces519B水题
  • 原文地址:https://www.cnblogs.com/jing-an-feng-shao/p/9316780.html
Copyright © 2020-2023  润新知