Một số kết quả kiểm tra
Tôi đã nhận được rất nhiều câu trả lời hay cho câu hỏi này - cảm ơn mọi người - vì vậy tôi quyết định thực hiện một số thử nghiệm và tìm ra phương pháp nào thực sự nhanh nhất. Năm phương pháp tôi đã thử nghiệm là:
- phương pháp "ContainsKey" mà tôi đã trình bày trong câu hỏi
- phương pháp "TestForNull" được đề xuất bởi Aleksandar Dimitrov
- phương pháp "AtomicLong" được đề xuất bởi Hank Gay
- phương pháp "Trove" được đề xuất bởi jrudolph
- phương pháp "MutableInt" được đề xuất bởi phax.myopenid.com
phương pháp
Đây là những gì tôi đã làm ...
- đã tạo ra năm lớp giống hệt nhau ngoại trừ sự khác biệt được hiển thị bên dưới. Mỗi lớp phải thực hiện một thao tác điển hình cho kịch bản tôi đã trình bày: mở tệp 10 MB và đọc nó, sau đó thực hiện đếm tần số của tất cả các mã thông báo từ trong tệp. Vì việc này chỉ mất trung bình 3 giây, tôi đã thực hiện việc đếm tần số (không phải I / O) 10 lần.
- đã tính thời gian của vòng lặp 10 lần nhưng không phải là thao tác I / O và ghi lại tổng thời gian thực hiện (tính bằng giây đồng hồ) về cơ bản bằng phương pháp của Ian Darwin trong Sách dạy nấu ăn Java .
- thực hiện tất cả năm bài kiểm tra theo chuỗi, và sau đó thực hiện thêm ba lần nữa.
- Tính trung bình bốn kết quả cho mỗi phương pháp.
Các kết quả
Tôi sẽ trình bày kết quả trước và mã dưới đây cho những ai quan tâm.
Các containsKey phương pháp được, như mong đợi, chậm nhất, vì vậy tôi sẽ cung cấp cho tốc độ của từng phương pháp so với tốc độ của phương pháp đó.
- Chứa khóa: 30.654 giây (đường cơ sở)
- AtomicLong: 29,780 giây (nhanh gấp 1,03 lần)
- TestForNull: 28,80 giây (nhanh gấp 1,06 lần)
- Trove: 26.313 giây (nhanh gấp 1,16 lần)
- MutableInt: 25,747 giây (nhanh gấp 1,19 lần)
Kết luận
Dường như chỉ có phương pháp MutableInt và phương pháp Trove nhanh hơn đáng kể, trong đó chỉ có chúng giúp tăng hiệu suất hơn 10%. Tuy nhiên, nếu phân luồng là một vấn đề, thì AtomicLong có thể hấp dẫn hơn các luồng khác (tôi không thực sự chắc chắn). Tôi cũng đã chạy TestForNull với final
các biến, nhưng sự khác biệt là không đáng kể.
Lưu ý rằng tôi chưa sử dụng bộ nhớ trong các tình huống khác nhau. Tôi rất vui khi được nghe từ bất kỳ ai có hiểu biết tốt về cách các phương thức MutableInt và Trove có thể ảnh hưởng đến việc sử dụng bộ nhớ.
Cá nhân, tôi thấy phương thức MutableInt hấp dẫn nhất, vì nó không yêu cầu tải bất kỳ lớp bên thứ ba nào. Vì vậy, trừ khi tôi phát hiện ra vấn đề với nó, đó là cách tôi thích nhất.
Mật mã
Đây là mã quan trọng từ mỗi phương pháp.
Chứa khóa
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);
}
Nguyên tử
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();
Quân đội
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();
}