Деякі результати тесту
Я отримав багато хороших відповідей на це питання - дякую людям - тому вирішив пройти кілька тестів і з’ясувати, який метод насправді найшвидший. Я перевірив п'ять методів:
- метод "ContainsKey", який я представив у запитанні
- метод "TestForNull", запропонований Олександром Димитров
- метод "AtomicLong", запропонований Генком Гей
- метод "Trove", запропонований jrudolph
- метод "MutableInt", запропонований phax.myopenid.com
Метод
Ось що я зробив ...
- створили п'ять класів, які були ідентичними, за винятком відмінностей, показаних нижче. Кожен клас повинен був виконати операцію, типову для представленого мною сценарію: відкрити файл 10 МБ і прочитати його, а потім виконати підрахунок частоти всіх символів слова у файлі. Оскільки це займало в середньому лише 3 секунди, я мав його виконувати підрахунок частоти (не введення / виводу) 10 разів.
- присвоїв цикл 10 ітерацій, але не операцію вводу / виводу, і записав загальний час (за годинникові секунди), по суті, використовуючи метод Яна Дарвіна в Java Cookbook .
- виконував усі п’ять тестів серійно, а потім робив це ще три рази.
- усереднено чотири результати для кожного методу.
Результати
Спочатку я представлю результати та код нижче для тих, хто цікавиться.
Метод 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);
}
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();
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; // 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();
}