ผลการทดสอบบางอย่าง
ฉันได้รับคำตอบที่ดีมากสำหรับคำถามนี้ - ขอบคุณผู้คน - ดังนั้นฉันจึงตัดสินใจทดสอบและหาวิธีที่เร็วที่สุด ห้าวิธีที่ฉันทดสอบคือ:
- วิธีการ "containKey" ที่ฉันนำเสนอในคำถาม
- วิธี "TestForNull" ที่แนะนำโดย Aleksandar Dimitrov
- วิธีการ "AtomicLong" แนะนำโดย Hank Gay
- วิธี "Trove" ที่แนะนำโดย jrudolph
- วิธีการ "MutableInt" แนะนำโดย phax.myopenid.com
วิธี
นี่คือสิ่งที่ฉันทำ ...
- สร้างห้าคลาสที่เหมือนกันยกเว้นความแตกต่างที่แสดงด้านล่าง แต่ละชั้นจะต้องดำเนินการตามปกติของสถานการณ์ที่ฉันนำเสนอ: เปิดไฟล์ 10MB และอ่านมันจากนั้นทำการนับความถี่ของโทเค็นคำทั้งหมดในไฟล์ เนื่องจากใช้เวลาเฉลี่ยเพียง 3 วินาทีฉันจึงทำการนับความถี่ (ไม่ใช่ I / O) 10 ครั้ง
- หมดเวลาห่วง 10 ซ้ำ แต่ไม่ได้ที่ผมดำเนินการ / Oและบันทึกเวลาทั้งหมดที่นำ (วินาทีนาฬิกา) เป็นหลักโดยใช้วิธีเอียนดาร์วินใน Java ตำรา
- ดำเนินการทดสอบทั้งห้าชุดตามลำดับจากนั้นทำเช่นนี้อีกสามครั้ง
- เฉลี่ยสี่ผลลัพธ์สำหรับแต่ละวิธี
ผล
ฉันจะแสดงผลลัพธ์ก่อนและรหัสด้านล่างสำหรับผู้ที่สนใจ
ContainsKeyวิธีการได้รับเป็นไปตามคาดที่ช้าที่สุดดังนั้นฉันจะให้ความเร็วของแต่ละวิธีในการเปรียบเทียบกับความเร็วของวิธีการว่า
- ContainsKey:คีย์ 30.654 วินาที (พื้นฐาน)
- AtomicLong: 29.780 วินาที (เร็วเป็น 1.03 เท่า)
- TestForNull: 28.804 วินาที (เร็วเป็น 1.06 เท่า)
- ขุม: 26.313 วินาที (เร็วที่สุด 1.16 เท่า)
- MutableInt: 25.747 วินาที (เร็ว 1.19 เท่า)
สรุปผลการวิจัย
ดูเหมือนว่าจะมีเพียงวิธี MutableInt และวิธี Trove เท่านั้นที่จะเร็วขึ้นอย่างมากโดยเฉพาะพวกเขาที่ให้ประสิทธิภาพการทำงานมากกว่า 10% อย่างไรก็ตามหากการทำเกลียวเป็นปัญหา AtomicLong อาจจะน่าดึงดูดกว่าตัวอื่น ๆ (ฉันไม่แน่ใจจริงๆ) ฉันยังใช้ TestForNull ด้วยfinal
ตัวแปร แต่ความแตกต่างนั้นเล็กน้อย
โปรดทราบว่าฉันไม่ได้ใช้หน่วยความจำประวัติในสถานการณ์ที่แตกต่างกัน ฉันยินดีที่จะได้ยินจากใครก็ตามที่มีความเข้าใจอย่างถ่องแท้เกี่ยวกับวิธีที่ 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);
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();
ขุม
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();
}