Некоторые результаты теста
Я получил много хороших ответов на этот вопрос - спасибо, ребята - поэтому я решил провести несколько тестов и выяснить, какой метод на самом деле самый быстрый. Пять методов, которые я протестировал, таковы:
- метод "ContainsKey", который я представил в вопросе
- метод «TestForNull», предложенный Александром Димитровым
- метод «AtomicLong», предложенный Хэнком Гей
- метод "Trove", предложенный Джрудолом
- метод "MutableInt", предложенный phax.myopenid.com
метод
Вот что я сделал ...
- создал пять классов, которые были идентичны, за исключением различий, показанных ниже. Каждый класс должен был выполнить операцию, типичную для сценария, который я представил: открыть файл 10 МБ и прочитать его, затем выполнить подсчет частоты всех жетонов слов в файле. Так как это заняло в среднем всего 3 секунды, мне пришлось выполнять подсчет частоты (не I / O) 10 раз.
- рассчитал цикл из 10 итераций, но не операции ввода-вывода, и записал общее время, затраченное (в секундах), по существу, используя метод Яна Дарвина в поваренной книге 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();
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();
}