你也许经常需要一个计数器来了解数据库或文本文件中一些事务出现的频率(例如单词)。通过在Java中计数器可以通过HashMap可以轻松实现计数器。本文比较了实现不同的计数器方法。

更新: 查看Java8 计数器,写一个计数器现在只是简单的2行代码。

1.基本计数器

朴素计数器可以如下实现:

String s = "one two three two three three";

String[] sArr = s.split(" ");

//naive approach

HashMap counter = new HashMap();

for (String a : sArr) {

if (counter.containsKey(a)) {

int oldValue = counter.get(a);

counter.put(a, oldValue + 1);

} else {

counter.put(a, 1);

}

}

在每个循环中,你检查key是否存在。如果是,则将旧值增加1,否则将其设置为1,这种方法很简单直接,但是它不是最有效的方法,由于以下方法,该方法被认为是低效的。

containsKey(),get()在一个key存在时候,被调用了两次,这意味了搜索了Map两次。

既然Integer是不可变的,每次循环将创建一个新的值来替代增加的旧值。

2.更好的计数器

自然地,我们希望有一个可变的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;

}

//used to print value convinently

public String toString(){

return Integer.toString(val);

}

}

计数器代码改变如下:

HashMap newCounter = new HashMap();

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对象。但是存在key键,仍然搜索两次。

3. 更高效计数器

HashMap.put(key, value) 方法返回键的当前值。这是有用的,因为我们可以使用旧值的引用来更新值,而不用再搜索一次。

HashMap efficientCounter = new HashMap();

for (String a : sArr) {

MutableInteger initValue = new MutableInteger(1);

MutableInteger oldValue = efficientCounter.put(a, initValue);

if(oldValue != null){

initValue.set(oldValue.get() + 1);

}

}

4.性能表现差异

为测试三种不同方法的性能,使用下面的代码,性能测试100万次,原始结果如下:

Naive Approach : 222796000

Better Approach: 117283000

Efficient Approach: 96374000

差别是显著的,223 vs 117 vs 96 基础计数器和高效计数器相差巨大,说明创建对象是昂贵的。

String s = "one two three two three three";

String[] sArr = s.split(" ");

long startTime = 0;

long endTime = 0;

long duration = 0;

// naive approach

startTime = System.nanoTime();

HashMap counter = new HashMap();

for (int i = 0; i < 1000000; i++)

for (String a : sArr) {

if (counter.containsKey(a)) {

int oldValue = counter.get(a);

counter.put(a, oldValue + 1);

} else {

counter.put(a, 1);

}

}

endTime = System.nanoTime();

duration = endTime - startTime;

System.out.println("Naive Approach : " + duration);

// better approach

startTime = System.nanoTime();

HashMap newCounter = new HashMap();

for (int i = 0; i < 1000000; i++)

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));

}

}

endTime = System.nanoTime();

duration = endTime - startTime;

System.out.println("Better Approach: " + duration);

// efficient approach

startTime = System.nanoTime();

HashMap efficientCounter = new HashMap();

for (int i = 0; i < 1000000; i++)

for (String a : sArr) {

MutableInteger initValue = new MutableInteger(1);

MutableInteger oldValue = efficientCounter.put(a, initValue);

if (oldValue != null) {

initValue.set(oldValue.get() + 1);

}

}

endTime = System.nanoTime();

duration = endTime - startTime;

System.out.println("Efficient Approach: " + duration);

在你进行计数的时候,可能需要Map的按值排序,可以看下Map按照值排序.

5.来自Keith解决方案

添加了几个测试:

1) 重构“更好方法”只是调用get而不是containsKey,通常你想要的元素在HashMap,只需要搜索一次。

2)添加一个AtomicInteger来进行测试,

3)与单独的int数组相比,根据http://amzn.com/0748614079使用较少的内存

我运行测试程序三次,并且采取最小的值,以消除与其他程序的方差。注意,你不能在程序中这样做,这样结果可能受GC影响而不同。

Naive: 201716122

Better Approach: 112259166

Efficient Approach: 93066471

Better Approach (without containsKey): 69578496

Better Approach (without containsKey, with AtomicInteger): 94313287

Better Approach (without containsKey, with int[]): 65877234

更好的办法(没有containsKey):

HashMap efficientCounter2 = new HashMap();

for (int i = 0; i < NUM_ITERATIONS; i++) {

for (String a : sArr) {

MutableInteger value = efficientCounter2.get(a);

if (value != null) {

value.set(value.get() + 1);

} else {

efficientCounter2.put(a, new MutableInteger(1));

}

}

}

更好的办法(没有containsKey,用AutomicInteger):

HashMap atomicCounter = new HashMap();

for (int i = 0; i < NUM_ITERATIONS; i++) {

for (String a : sArr) {

AtomicInteger value = atomicCounter.get(a);

if (value != null) {

value.incrementAndGet();

} else {

atomicCounter.put(a, new AtomicInteger(1));

}

}

}

更好的方法(没用containsKey ,用int[]):

HashMap intCounter = new HashMap();

for (int i = 0; i < NUM_ITERATIONS; i++) {

for (String a : sArr) {

int[] valueWrapper = intCounter.get(a);

if (valueWrapper == null) {

intCounter.put(a, new int[] { 1 });

} else {

valueWrapper[0]++;

}

}

}

Guava的MultiSet可能更快。

6 .结论

计数器效率比较

java 计数器怎么定义_4.7 JAVA计数器相关推荐

  1. java标识符与关键字_4、Java标识符和关键字

    标识符:Java对各种变量,方法和类等要素命名时使用的字符序列称为标识符.(凡是自己可以起名的地方都叫标识符,都遵循标识符的规则) Java的命名规则: 1.标识符由字母.下划线"_&quo ...

  2. java 机机接口定义_【JAVA】接口

    先问一个问题,为什么需要接口? 看例子: 需求:要求实现防盗门的功能.门有"开"和"关"的功能,所有"上锁"和"开锁"的 ...

  3. Java中怎么定义字符串?Java基础

    字符串是 Java 中特殊的类,使用方法像一般的基本数据类型,被广泛应用在 Java 编程中.Java 没有内置的字符串类型,而是在标准 Java 类库中提供了一个 String 类来创建和操作字符串 ...

  4. java for循环定义变量,在java语言里for循环里的变量如何声明在外面进行使用。

    在java语言里for循环里的变量如何声明在外面进行使用. 关注:142  答案:2  手机版 解决时间 2021-02-01 21:59 提问者懷念那年夏天 2021-01-31 21:11 pub ...

  5. Android 价值千万java多线程同步 lt;五CountDownLatch(计数器)和Semaphore(信号量)

    1).Android 价值千万   java线程专题:Wait&notify&join&Yield http://blog.csdn.net/whb20081815/artic ...

  6. java run里面定义变量_Java程序员50多道最热门的多线程和并发面试题(答案解析)...

    下面是Java程序员相关的热门面试题,你可以用它来好好准备面试. 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器 ...

  7. java设计模式中不属于创建型模式_23种设计模式第二篇:java工厂模式定义:工厂模式是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式...

    23种设计模式第二篇:java工厂模式 定义: 工厂模式是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 工厂模式主要是为创建对象提供过渡接口, ...

  8. java接口如何定义常量 c_在Java接口中怎样访问定义的常量呢?

    java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能).那么我们在Java接口中怎 ...

  9. java类的定义的实例_Java类的定义与实例化

    本节着重讲解Java语法,不再对类和对象的概念进行深入阐述,如果你不了解类与对象的基本概念,请查看这里:类与对象的基本概念.面向对象编程(OOP)的概念. Java类的定义 在Java中,类的定义语法 ...

最新文章

  1. WPF学习拾遗(二)TextBlock换行
  2. ArcGIS10新功能之制作地图集
  3. Worker Service in ASP .NET Core
  4. mongo数据库 备份 还原
  5. linux hiredis升级,Redis平滑升级
  6. java反编译工具jad和jd-gui使用
  7. c 更新mysql数据_MySQL插入更新删除数据
  8. 【C++算法基础】快速排序以及边界问题
  9. Linux卸载JDK的方法
  10. 解决方案|致拓T8数字化ERP
  11. dns服务器未响应韩国,上海联通DNS
  12. 网络流量分析——NPMD关注IT运维、识别宕机和运行不佳进行性能优化。智能化分析是关键-主动发现业务运行异常。科来做APT相关的安全分析...
  13. django-查询语句(一)
  14. JSP页面请求和响应
  15. 【一起入门NLP】中科院自然语言处理作业一:RNN,DNN,CNN 进行猫狗分类(pytorch入门)【代码+报告】
  16. ip_rcv ip_rcv_finish
  17. 软件工程导论第九到十二章章节复习总结附思维导图
  18. linux抓bpdu包,用Python发送手工构建的BPDU(生成树协议包)
  19. linux 动态监控进程
  20. JavaScript闭包的个人理解

热门文章

  1. v-charts 多个环形图
  2. 用c++写一个简单的图书管理系统
  3. cashe(缓存)【转】
  4. Ubuntu桌面美化(unity-tweak-tool)
  5. Oracle通过kafka同步数据到MySQL
  6. IPV6前缀变化设置通过防火墙的方法
  7. 【编译原理】中间代码优化(二) 局部优化
  8. Matter Over Thread实战教程-Silicon Labs EFR32-2-01:官方Demo硬件准备
  9. 【XLL API 函数】xlSheetNm
  10. java毕业设计美容院管理系统Mybatis+系统+数据库+调试部署