zoukankan      html  css  js  c++  java
  • 在java中高效的计数器

    在编程中,经常会用到HashMap作为计数器,本文简单介绍三种实现方式

    第一种,最直观的计数器。

    public void naiveCounter(String sArr[]) {
            HashMap<String, Integer> counter = new HashMap<String, Integer>();
            for (String a : sArr) {
                if (counter.containsKey(a)) {
                    int oldValue = counter.get(a);
                    counter.put(a, oldValue + 1);
                } else {
                    counter.put(a, 1);
                }
            }
        }

    在这种方式中,每次循环都去检查Map中是否包含Key,如果包含则将原值+1再保存,如果不存在,则直接保存1.这种方式是最简单直接的一种方式,但是并不是最有效的方式,其低效的原因:

    1、如果已经存在某个key(a),containsKey()和get()方法会扫描Map两次

    2、由于Integer是不可变对象,因此每次循环,都会创建一个新的对象放到Map中。

    第二种,比较好的计数器

    由于上述原因,自然可以想到创建一个可变的Integer对象,这样可以避免创建过多的Integer对象,

    可变的Integer对象定义如下:

    class MutableInteger {
            private int val;
    
            public MutableInteger(int val) {
                this.val = val;
            }
    
            public int get() {
                return val;
            }
    
            public void set(int val) {
                this.val = val;
            }
    
            public String toString() {
                return Integer.toString(val);
            }
        }

    然后将上面的计数器优化一下,改成下面这张格式

    public void betterCounter(String[] sArr) {
            HashMap<String, MutableInteger> newCounter = new HashMap<String, MutableInteger>();
            for (String a : sArr) {
                if (newCounter.containsKey(a)) {
                    MutableInteger oldValue = newCounter.get(a);
                    oldValue.set(oldValue.get() + 1);
                } else {
                    newCounter.put(a, new MutableInteger(1));
                }
            }
        }

    第三种,高效的计数器

    上面第二种已经解决了创建过多Integer对象的问题,但是扫描两次Map,

    其实在HashMap中,存在一个能够返回当前值的方法(HashMap.put(key,value)),

    在上面的基础上,我们可以通过对原来的引用进行更新,而不必扫描两次Map

    public void efficientCounter(String[] sArr) {
            HashMap<String, MutableInteger> efficientCounter = new HashMap<String, MutableInteger>();
            for (String a : sArr) {
                MutableInteger initValue = new MutableInteger(1);
                MutableInteger oldValue = efficientCounter.put(a, initValue);
                 //MutableInteger是可变的,此处只需判断之前是否为空,
                 // 如果非空,则更新MutableInteger的引用即可
                if (oldValue != null) {
                    initValue.set(oldValue.get() + 1);
                }
            }
        }


    下面是经过500W次操作后,三者方法所消耗的时间

    start test naiveCounter
    end test naiveCounter,time is:5629


    start test betterCounter
    end test betterCounter,time is:5030


    start test efficientCounter
    end test efficientCounter,time is:4418

    可以看出,最后一种确实是三种里面最高效的方式

  • 相关阅读:
    Java :BufferedWriter类和BufferedReader类的构造方法、主要方法
    多线程:四大线程池详解
    多线程:head first Thread.join()
    多线程: 多线程中断机制
    多线程:深入Thread.sleep
    多线程:线程池原理
    java:java构造器和java方法的区别
    java: Java中this和super的用法总结
    postman: 用于网页调试和发送Http请求的chrome插件
    json:JSONObject包的具体使用(JSONObject-lib包是一个beans,collections,maps,java arrays和xml和JSON互相转换的包)
  • 原文地址:https://www.cnblogs.com/fuhaots2009/p/3433275.html
Copyright © 2011-2022 走看看