在Java中增加Map值的最有效的方式

问题:

我希望这个问题在这个论坛上不被认为太基础,但我们会看到。我想知道如何重构一些代码,以获得更好的性能,这是一段时间。
说我正在创建一个单词频率列表,使用一个Map(可能是一个HashMap),其中每个键都是一个字符串,该单词正在计数,该值是一个Integer,每次发现一个单词的标记时会增加。
在Perl中,增加这样一个值会很简单:

$map{$word}++;

但是在Java中,它要复杂得多。这里我正在这样做:

int count = map.containsKey(word) ? map.get(word) : 0;
map.put(word, count + 1);

当然,这些更新的Java版本依赖于自动装箱功能。我想知道你能否提出一种更有效的方式来增加这样的价值。有没有很好的性能原因避免收集框架和使用其他的东西?
更新:我已经对几个答案进行了测试。见下文。

回答:

一些测试结果

我已经得到了很多很好的答案,谢谢各位,所以我决定运行一些测试,并找出哪种方法实际上是最快的。我测试的五种方法是:

  • 我在the question中提供的“ContainsKey”方法
  • Aleksandar Dimitrov建议的“TestForNull”方法
  • Hank Gay建议的“AtomicLong”方法
  • jrudolph建议的“Trove”方法
  • phax.myopenid.com建议的“MutableInt”方法

方法

这是我做的

  1. 创建了五个相同的类,除了下面的差异。每个类都必须执行典型的我所呈现的场景的操作:打开一个10MB的文件并读入,然后执行文件中所有单词令牌的频率计数。由于平均需要3秒钟,所以我执行频率计数(不是I / O)10次。
  2. 定时10次迭代但not the I/O operation的循环,并记录基本上使用Ian Darwin’s method in the Java Cookbook所需的总时间(以秒为单位)。
  3. 进行了所有五个系列的测试,然后再做了三次。
  4. 平均每种方法的四个结果。

结果

我将首先介绍结果,并给出有兴趣的人员下面的代码。
正如预期的那样,的containsKey方法是最慢的,所以我将给出每种方法的速度与该方法的速度相比。

  •  的containsKey: 30.654秒(基线)
  •  AtomicLong的: 29.780秒(1.03倍快)
  •  TestForNull: 28.804秒(1.06倍快)
  •  特罗韦: 26.313秒(1.16倍快)
  •  MutableInt: 25.747秒(1.19倍快)

结论

似乎只有MutableInt方法和Trove方法显着更快,因为只有它们的性能提升超过10%。然而,如果线程是一个问题,AtomicLong可能比其他人更有吸引力(我不太确定)。我也运行TestForNull与final变量,但差异是微不足道的。
请注意,我没有在不同的场景中分析内存使用情况。对于有关MutableInt和Trove方法可能会影响内存使用情况的人士,我将很乐意听到任何人的深入了解。
就个人而言,我发现MutableInt方法是最具吸引力的,因为它不需要加载任何第三方类。所以除非我发现问题,那就是我最有可能走的路。

代码

这是每个方法的关键代码。

的containsKey

import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
int count = freq.containsKey(word) ? freq.get(word) : 0;
freq.put(word, count + 1);

TestForNull

import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
Integer count = freq.get(word);
if (count == null) {
    freq.put(word, 1);
}
else {
    freq.put(word, count + 1);
}

的AtomicLong

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

特罗韦

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

MutableInt

import java.util.HashMap;
import java.util.Map;
...
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();
}

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: Most efficient way to increment a Map value in Java

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

6 + 4 =