いくつかのテスト結果
私はこの質問に対して多くの良い答えを得ました - 皆さんに感謝します - そこで私はいくつかのテストを実行し、どの方法が実際に最速であるかを理解することにしました. 私がテストした5つの方法は次のとおりです。
- 質問で提示した「ContainsKey」メソッド
- Aleksandar Dimitrov によって提案された "TestForNull" メソッド
- ハンク・ゲイが提案した「AtomicLong」メソッド
- jrudolph によって提案された "Trove" メソッド
- phax.myopenid.com によって提案された「MutableInt」メソッド
方法
これが私がやったことです...
- 以下に示す違いを除いて同一の 5 つのクラスを作成しました。各クラスは、私が提示したシナリオに典型的な操作を実行する必要がありました。10 MB のファイルを開いて読み込み、ファイル内のすべての単語トークンの頻度カウントを実行しました。平均で 3 秒しかかからなかったので、(I/O ではなく) 頻度カウントを 10 回実行しました。
- I/O 操作ではなく10 回の反復のループの時間を計り、Java Cookbook の Ian Darwin の方法を基本的に使用して、合計所要時間 (クロック秒単位) を記録しました。
- 一連の 5 つのテストすべてを実行し、これをさらに 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 が他のものよりも魅力的かもしれません (よくわかりません)。変数を指定して TestForNull も実行しましfinal
たが、違いはごくわずかでした。
さまざまなシナリオでメモリ使用量をプロファイリングしていないことに注意してください。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);
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();
}