关于如何用java实现一个高效的计数器

本文探讨了在编程中高效实现单词统计个数的方法,包括使用HashMap、AtomicLong、Trove库及MutableInt类,并对比了它们的时间效率。重点介绍了为何某些方法比传统HashMap更为高效,以及如何通过内存旋转等技术进一步优化性能。

最近在写毕设的时候遇到的一个,很常见的问题

就是对单词统计个数

stackoverflow上的解答

关于如何高效的实现一般有下面几种方法:

[1]使用hashmap

但是注意不要使用containsKey(X) 来判断是否已经事先存在某个word 这会导致每次都遍历整个map

可以使用get(X)==null 来判断是否存在了该单词 这样更快

Integer count = map.get(word);
	if(count == null){
		count = 0;
	}
map.put(word, count + 1);


[2]使用AtomicLong 

final ConcurrentMap<String, AtomicLong> map = 
    new ConcurrentHashMap<String, AtomicLong>();
...
map.putIfAbsent(word, new AtomicLong(0));
map.get(word).incrementAndGet();


[3]使用trove【high perfomance for java collections】

TObjectIntHashMap<String> freq = new TObjectIntHashMap<String>();
...
freq.adjustOrPutValue(word, 1, 1);


[4]使用MutableInt

至于这个为什么会比HashMap<String,Integer> 快的原因 还要慢慢寻找?

class MutableInt {
  int value = 1; // note that we start at 1 since we're counting
  public void increment () { ++value;      }
  public int  get ()       { return value; }
}
...
Map<String, MutableInt> freq = new HashMap<String, MutableInt>();
...
MutableInt count = freq.get(word);
if (count == null) {
    freq.put(word, new MutableInt());
}
else {
    count.increment();
}

======结论:

  • ContainsKey: 30.654 seconds (baseline)
  • TestForNull: 28.804 seconds (1.06 times as fast)
  • AtomicLong: 29.780 seconds (1.03 times as fast)
  • Trove: 26.313 seconds (1.16 times as fast)
  • MutableInt: 25.747 seconds (1.19 times as fast)
可以看见MutableInt 尽然是最快的(ps:apache commons组件实现了该类)还在思索中。。。。求高人解答(为何比<String,Integer>)还要快?

下面可能是原因:理解中~

Memory rotation may be an issue here, since every boxing of an int larger than or equal to 128 causes an object allocation (see Integer.valueOf(int)). Although the garbage collector very efficiently deals with short-lived objects, performance will suffer to some degree.

If you know that the number of increments made will largely outnumber the number of keys (=words in this case), consider using an int holder instead. Phax already presented code for this. Here it is again, with two changes (holder class made static and initial value set to 1):

static class MutableInt {
  int value = 1;
  void inc() { ++value; }
  int get() { return value; }
}
...
Map<String,MutableInt> map = new HashMap<String,MutableInt>();
MutableInt value = map.get(key);
if (value == null) {
  value = new MutableInt();
  map.put(key, value);
} else {
  value.inc();
}

If you need extreme performance, look for a Map implementation which is directly tailored towards primitive value types. jrudolph mentioned GNU Trove.

By the way, a good search term for this subject is "histogram".


======

转载于:https://www.cnblogs.com/scugxl/archive/2013/03/25/4131799.html

### Java 实现计数器的示例代码 以下是通过纯 Java 和 Redis 两种方式实现计数器的示例代码。 #### 方法一:纯 Java 实现计数器 以下是一个简单的计数器类 `Counter` 的实现,其中包含了初始化、增加计数值和获取当前计数值的功能[^2]: ```java public class Counter { private int count = 0; // 增加计数 public synchronized void increment() { count++; } // 获取当前计数 public int getCount() { return count; } // 重置计数 public void reset() { this.count = 0; } } ``` 上述代码中,`increment()` 方法被标记为 `synchronized`,以确保在多线程环境下能够安全地更新共享资源[^3]。 --- #### 方法二:基于 Redis 的分布式计数器 当需要处理高并发场景下的计数需求时,可以考虑使用 Redis 来存储和管理计数值。Redis 提供了原子性的自增操作 (`INCR`) 和批量操作能力,非常适合用于构建高效计数器[^4]。 以下是一个基于 Redis 的计数器实现示例代码[^5]: ```java import redis.clients.jedis.Jedis; public class RedisCounter { private Jedis jedis; private String key; public RedisCounter(String host, int port, String counterKey) { this.key = counterKey; this.jedis = new Jedis(host, port); } // 增加计数 public long increment() { return jedis.incr(this.key); // 使用 INCR 命令进行原子性递增 } // 获取当前计数 public long getCount() { String value = jedis.get(this.key); return (value != null) ? Long.parseLong(value) : 0L; } // 重置计数 public void reset() { jedis.set(this.key, "0"); } // 关闭连接 public void close() { if (jedis != null) { jedis.close(); } } } ``` 在这个例子中,`incr` 是 Redis 提供的一个命令,它可以对指定键的值进行原子性递增操作。这种方式特别适合于分布式的高并发环境,因为它避免了传统数据库锁带来的性能瓶颈。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值