java 计数器怎么定义_4.7 JAVA计数器

你也许经常需要一个计数器来了解数据库或文本文件中一些事务出现的频率(例如单词)。通过在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 .结论

d830cf84eb04

计数器效率比较

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值