部分测试结果
我已经得到了很多关于这个问题的好答案——谢谢大家——所以我决定运行一些测试并找出哪种方法实际上是最快的。我测试的五种方法是:
- 我在the question 中介绍的“ContainsKey”方法
- Aleksandar Dimitrov 建议的“TestForNull”方法
- Hank Gay 建议的“AtomicLong”方法
- jrudolph 建议的“Trove”方法
- phax.myopenid.com 建议的“MutableInt”方法
方法
这就是我所做的......
- 创建了五个相同的类,除了下面显示的差异。每个类都必须执行我提出的场景的典型操作:打开一个 10MB 的文件并读入它,然后对文件中的所有单词标记执行频率计数。由于这平均只需要 3 秒,因此我让它执行了 10 次频率计数(而不是 I/O)。
- 对 10 次迭代的循环进行计时,但不是 I/O 操作,并基本上使用 Ian Darwin's method in the Java Cookbook 记录所花费的总时间(以时钟秒为单位)。
- 连续执行了所有五项测试,然后又执行了 3 次。
- 对每种方法的四个结果进行平均。
结果
我先把结果和下面的代码展示给有兴趣的人。
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 方法最有吸引力,因为它不需要加载任何第三方类。所以除非我发现它有问题,否则我最有可能这样做。
代码
这是每个方法的关键代码。
包含密钥
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);
}
原子长
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);
可变整数
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();
}