一些测试结果
我得到了很多关于这个问题的好答案——感谢大家——所以我决定运行一些测试,找出哪种方法实际上是最快的。我测试过的五种方法是:
- 我在这个问题中提出的"ContainsKey"方法
- Aleksandar Dimitrov建议的"TestForNull"方法
- Hank Gay建议的"AtomicLong"方法
- jrudolph建议的"Trove"方法
- phax.myopenid.com建议的"MutableInt"方法
方法
以下是我的做法...
- 创建了五个除不同之处外完全相同的类。每个类都必须执行一个场景中典型的操作:打开一个10MB的文件并将其读入,然后对文件中所有单词令牌进行频率计数。由于这只需要平均3秒钟,所以我让它执行频率计数(而不是I/O)10次。
- 计时10次迭代的循环但不计算I/O操作,并记录总共花费的时间(以时钟秒为单位),基本上使用了Java Cookbook中Ian Darwin的方法。
- 依次执行所有五个测试,然后再重复三次。
- 对每种方法的四个结果求平均值。
结果
我会先呈现结果,然后是下面有兴趣的人的代码。
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方法最具吸引力,因为它不需要加载任何第三方类。所以,除非我发现它存在问题,否则我最有可能采用这种方法。
代码
以下是每种方法的关键代码。
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);
测试 Null 值
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();
Trove
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;
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();
}
Map::computeIfAbsent
及其相关方法。 - Brian Goetz