在Java中增加映射值的最有效方法

我希望这个问题对于这个论坛来说不是太基本,但我们会看到的。我想知道如何重构一些代码以获得更好的性能,这些代码已经运行了很多次了

假设我正在使用一个映射(可能是一个HashMap)创建一个单词频率列表,其中每个键都是一个字符串,其中包含正在计数的单词,值是一个整数,每次找到单词的标记时,该值都会递增

在Perl中,增加这样的值非常容易:

$map{$word}++;

但在Java中,它要复杂得多。以下是我目前的做法:

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

这当然依赖于较新Java版本中的自动装箱功能。我想知道你能否提出一种更有效的方法来增加这样一个值。是否有很好的性能原因可以避免使用集合框架而使用其他框架

更新:我已经对几个答案进行了测试。见下文

一些测试结果

对于这个问题,我得到了很多很好的答案——谢谢各位——所以我决定运行一些测试,找出哪种方法实际上最快。我测试的五种方法如下:

  • 我在问题
  • Aleksandar Dimitrov建议的“TestForNull”方法
  • Hank Gay提出的“原子长”方法
  • jrudolph建议的“Trove”方法
  • phax.myopenid.com建议的“MutableInt”方法

方法

这就是我所做的

  1. 创建了五个相同的类,除了下面显示的差异。每个类都必须执行我介绍的场景中的典型操作:打开一个10MB的文件并读入它,然后对文件中的所有单词标记执行频率计数。因为这平均只花了3秒钟,所以我让它执行了10次频率计数(不是I/O)
  2. 对10次迭代的循环进行计时,但不是I/O操作,并使用Java烹饪书中的Ian Darwin方法
  3. 在系列中执行所有五个测试,然后再执行三次
  4. 对每种方法的四个结果取平均值

结果

我将首先向感兴趣的人展示结果和下面的代码

正如所料,ContainsKey方法是最慢的,因此我将给出每个方法的速度,并与该方法的速度进行比较

  • ContainsKey:30.654秒(基线)
  • AtomicLong:29.780秒(速度的1.03倍)
  • TestForNull:28.804秒(速度的1.06倍)
  • Trove:26.313秒(速度的1.16倍)
  • MutableInt:25.747秒(速度的1.19倍)

结论

似乎只有MutableInt方法和Trove方法的速度要快得多,因为只有它们的性能提升才超过10%。但是,如果线程是一个问题,那么AtomicLong可能比其他线程更有吸引力(我不太确定)。我还使用final变量运行了TestForNull,但差异可以忽略不计

请注意,我没有分析不同场景中的内存使用情况。我很高兴听到有人对MutableInt和Trove方法可能如何影响内存使用有很好的见解

我个人认为MutableInt方法最有吸引力,因为它不需要加载任何第三方类。所以除非我发现它有问题,否则我很可能会这样做

代码

下面是每个方法的关键代码

康纳斯基

导入java.util.HashMap;
导入java.util.Map;

地图<字符串,整数>freq=新HashMap<字符串,整数>();

int count=freq.containsKey(字)?freq.get(word):0;
频率输出(字,计数+1);

TestForNull

导入java.util.HashMap;
导入java.util.Map;

地图<字符串,整数>freq=新HashMap<字符串,整数>();

整数计数=freq.get(字);
如果(计数=null){
频率输出(字,1);
}
否则{
频率输出(字,计数+1);
}

原子长

导入java.util.concurrent.ConcurrentHashMap;
导入java.util.concurrent.ConcurrentMap;
导入java.util.concurrent.AtomicLong;

最终ConcurrentMap<字符串,原子长度>地图=
新ConcurrentHashMap<字符串,原子长度>();

图.putIfAbsent(单词,新原子长(0));
map.get(word.incrementAndGet();

宝藏

导入gnu.trove.tobjectnthashmap;
...
TObjectIntHashMap<字符串>freq=新的Tobjectnthashmap<字符串>();
...
频率调节器输出值(字,1,1);

可变点

导入java.util.HashMap;
导入java.util.Map;

类可变整型{
int value=1;//注意,我们从1开始计数
公共无效增量(){++value;}
public int get(){return value;}
}

地图<字符串,可变整型>freq=新HashMap<字符串,可变整型>();

MutableInt count=freq.get(word);
如果(计数=null){
freq.put(word,new MutableInt());
}
否则{
count.increment();
}

发表评论