Beberapa hasil tes
Saya mendapatkan banyak jawaban yang bagus untuk pertanyaan ini - terima kasih semuanya - jadi saya memutuskan untuk menjalankan beberapa tes dan mencari tahu metode mana yang sebenarnya tercepat. Lima metode yang saya uji adalah:
- metode "ContainsKey" yang saya sajikan dalam pertanyaan
- metode "TestForNull" yang disarankan oleh Aleksandar Dimitrov
- metode "AtomicLong" yang disarankan oleh Hank Gay
- metode "Trove" yang disarankan oleh jrudolph
- metode "MutableInt" yang disarankan oleh phax.myopenid.com
metode
Inilah yang saya lakukan ...
- menciptakan lima kelas yang identik kecuali untuk perbedaan yang ditunjukkan di bawah ini. Setiap kelas harus melakukan operasi khas skenario yang saya sajikan: membuka file 10MB dan membacanya, lalu melakukan penghitungan frekuensi semua kata token dalam file. Karena ini mengambil rata-rata hanya 3 detik, saya sudah melakukan penghitungan frekuensi (bukan I / O) 10 kali.
- menghitung waktu loop 10 iterasi tetapi bukan operasi I / O dan mencatat total waktu yang diambil (dalam detik jam) pada dasarnya menggunakan metode Ian Darwin di Java Cookbook .
- melakukan semua lima tes secara seri, dan kemudian melakukan ini tiga kali lagi.
- rata-rata empat hasil untuk setiap metode.
Hasil
Saya akan mempresentasikan hasil pertama dan kode di bawah ini untuk mereka yang tertarik.
The ContainsKey metode itu, seperti yang diharapkan, paling lambat, jadi saya akan memberikan kecepatan setiap metode dibandingkan dengan kecepatan metode tersebut.
- ContainsKey: 30,654 detik (garis dasar)
- AtomicLong: 29,780 detik (1,03 kali lebih cepat)
- TestForNull: 28,804 detik (1,06 kali lebih cepat)
- Trove: 26,313 detik (1,16 kali lebih cepat)
- MutableInt: 25,747 detik (1,19 kali lebih cepat)
Kesimpulan
Tampaknya hanya metode MutableInt dan metode Trove yang secara signifikan lebih cepat, hanya mereka yang memberikan peningkatan kinerja lebih dari 10%. Namun, jika threading adalah masalah, AtomicLong mungkin lebih menarik daripada yang lain (saya tidak begitu yakin). Saya juga menjalankan TestForNull dengan final
variabel, tetapi perbedaannya dapat diabaikan.
Perhatikan bahwa saya belum membuat profil penggunaan memori dalam berbagai skenario. Saya akan senang mendengar dari siapa pun yang memiliki wawasan yang baik tentang bagaimana metode MutableInt dan Trove akan mempengaruhi penggunaan memori.
Secara pribadi, saya menemukan metode MutableInt yang paling menarik, karena tidak perlu memuat kelas pihak ketiga. Jadi, kecuali saya menemukan masalah dengan itu, itulah cara saya kemungkinan besar pergi.
Kode
Berikut adalah kode penting dari setiap metode.
Berisi kunci
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();
Harta karun
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();
}