您可能经常需要计数器来了解数据库或文本文件中某些内容(例如单词)的出现频率。通过使用Java中的HashMap可以轻松实现计数器。本文比较了实现计数器的不同方法。最后,将得出一个有效的结论。

java记次数 java中计数_数组

1.天真版计数器

天真版的,它可以通过以下方式实现:

String s = "one two three two three three";String[] sArr = s.split(" ");
//naive approach
HashMap counter = newHashMap();
for(String a : sArr) {
if(counter.containsKey(a)) {
intoldValue = counter.get(a);
counter.put(a, oldValue + 1);
} else{
counter.put(a, 1);
}}

在每个循环中,您检查密钥是否存在。如果是这样,则将旧值增加1;否则,将其设置为1。此方法简单明了,但这不是最有效的方法。由于以下原因,该方法的效率较低:

· 当一个键已经存在时,containsKey(),get()被调用两次。这意味着搜索地图两次。

· 由于Integer是不可变的,因此每个循环将创建一个新循环以增加旧值

2.更好的counter

自然地,我们想要一个可变的整数以避免创建许多Integer对象。可变整数类可以定义如下:

java记次数 java中计数_搜索_02

并将计数器改进并更改为以下内容:

java记次数 java中计数_java 计数_03

这似乎更好,因为它不再需要创建许多Integer对象。但是,如果存在键,则在每个循环中搜索仍然两次。

3.高效counter

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

java记次数 java中计数_数组_04

4.性能差异

为了测试三种不同方法的性能,使用了以下代码。性能测试是一百万次。原始结果如下:

天真的方法:222796000

更好的方法:117283000

高效的方法:96374000

差异非常明显-223 vs. 117 vs.96。Naive和Better之间存在巨大差异,这表明创建对象非常昂贵!

java记次数 java中计数_搜索_05

使用计数器时,可能还需要一个函数,以按值对映射进行排序。您可以查看HashMap的常用方法。

5.更好的解决方案

添加了一些测试:

1)重构了“更好的方法”,只调用get而不是containsKey。通常,您想要的元素都在HashMap中,以便从两次搜索减少到一次搜索。

2)添加了michal提到的AtomicInteger测试。

3)与单例int数组相比,使用较少的内存

我将测试程序运行了3倍,并花了最小的时间来消除其他程序的差异。请注意,您不能在程序中执行此操作,否则结果可能受gc影响太大。

天真:201716122

更好的方法:112259166

高效途径:93066471

更好的方法(没有containsKey):69578496

更好的方法(没有containsKey,带有AtomicInteger):94313287

更好的方法(没有containsKey,带有int []):65877234

更好的方法(没有containsKey):

java记次数 java中计数_数组_06

更好的方法(不包含containsKey,使用AtomicInteger):

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

AtomicInteger value = atomicCounter.get(a);

value.incrementAndGet();

atomicCounter.put(a, newAtomicInteger(1));

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

java记次数 java中计数_Java_07

6. 结论

获胜者是使用int数组的最后一个。