在编程中,经常会用到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中高效的计数器的更多相关文章

  1. 在 Java 中高效使用锁的技巧--转载

    竞争锁是造成多线程应用程序性能瓶颈的主要原因 区分竞争锁和非竞争锁对性能的影响非常重要.如果一个锁自始至终只被一个线程使用,那么 JVM 有能力优化它带来的绝大部分损耗.如果一个锁被多个线程使用过,但 ...

  2. 七、如何在Java中高效检查一个数组是否含有一个值

    如何检查一个数组(非排序的)是否包含特定的值.这是个非常有用或经常被在Java中使用.这是个在Stack Overflow中高得票的问题.在已经高得票的答案中,有许多不同的处理方法,但是时间的复杂度非 ...

  3. 161101、在Java中如何高效判断数组中是否包含某个元素

    如何检查一个数组(无序)是否包含一个特定的值?这是一个在Java中经常用到的并且非常有用的操作.同时,这个问题在Stack Overflow中也是一个非常热门的问题.在投票比较高的几个答案中给出了几种 ...

  4. 在Java中如何高效的判断数组中是否包含某个元素

    原文出处: hollischuang(@Hollis_Chuang) 如何检查一个数组(无序)是否包含一个特定的值?这是一个在Java中经常用到的并且非常有用的操作.同时,这个问题在Stack Ove ...

  5. 在Java中怎样高效的推断数组中是否包括某个元素

    来自 http://www.hollischuang.com/archives/1269? 怎样检查一个数组(无序)是否包括一个特定的值?这是一个在Java中经经常使用到的并且非常实用的操作.同一时候 ...

  6. 在java中构建高效的结果缓存

    文章目录 使用HashMap 使用ConcurrentHashMap FutureTask 在java中构建高效的结果缓存 缓存是现代应用服务器中非常常用的组件.除了第三方缓存以外,我们通常也需要在j ...

  7. 在Java中如何高效判断数组中是否包含某个元素

    如何检查一个数组(无序)是否包含一个特定的值?这是一个在Java中经常用到的并且非常有用的操作.同时,这个问题在Stack Overflow中也是一个非常热门的问题.在投票比较高的几个答案中给出了几种 ...

  8. Java中的GC操作及相关概念

    一.GC Roots Tracing的基本思路:通过一系列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所经过的路径称为引用链(Reference Chai ...

  9. 面试中关于Java中涉及到知识点(转)

    本篇文章会对面试中常遇到的Java技术点进行全面深入的总结,帮助我们在面试中更加得心应手,不参加面试的同学也能够借此机会梳理一下自己的知识体系,进行查漏补缺. 1. Java中的原始数据类型都有哪些, ...

随机推荐

  1. BZOJ 3944 Sum 解题报告

    我们考虑令: \[F_n = \sum_{d|n}\varphi(d)\] 那么,有: \[\sum_{i=1}^{n}F_i = \sum_{i=1}^{n}\sum_{d|i}\varphi(d) ...

  2. 1013: [JSOI2008]球形空间产生器sphere

    很直观的一个gauss题: 用的是以前用过的一个模板: #include<cstdio> #include<algorithm> #include<cmath> # ...

  3. unity 引用 移动mm 支付sdk

    原地址:http://blog.csdn.net/u012085988/article/details/17531005 资源下载:http://download.csdn.net/detail/u0 ...

  4. java Class的Long id初始化 为0的问题android数据库操做出现的 android.database.sqlite.SQLiteConstraintException: error code 19: constraint failed

    java的class中的Long类型变量调用默认的 构造函数new后会被初始化为0. 这句话大家可能感觉这么低级的事情还用你说? 我想说的是这个会产生的一个应用场景 和 避免方法 场景:db插入时候p ...

  5. C# Eval在asp.net中的用法及作用

    Eval( " ")和Bind( " ") 这两种一个单向绑定,一个双向绑定,bind是双向绑定,但需数据源支持 ASP.NET 2.0改善了模板中的数据绑定操 ...

  6. ANDROID_MARS学习笔记_S01原始版_006_ListView

    一.代码1.xml(1)main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayo ...

  7. 【Linux远程管理】Telnet远程连接管理

    Telnet,命令行界面下的远程管理工具,因为其历史非常悠久,几乎所有的操作系统都有该工具, 但是,Telnet在传输数据是是通过明文传输的,没有加密,所以现在几乎不会使用Telnet进行管理了. ( ...

  8. linux中fork()函数详解(原创!!实例讲解)

    一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...

  9. error: 'LOGE' was not declared in this scope

    移植了下HAL,发现编译出现如下错误 error: 'LOGE' was not declared in this scope 比较了一下android4.1的 system/core/include ...

  10. asp.net web 开发登录相关操作的控件LoginName、LoginStatus和LoginView控件使用详解

    http://book.51cto.com/art/200909/154039.htm http://book.51cto.com/art/200909/154041.htm ASP.NET提供了一套 ...