Какую реализацию Map <K, V> следует использовать, если моя карта должна быть маленькой, чем быстрее?
Я обычно использую HashMap
в своих программах, так как я знаю, что он обычно наиболее эффективен (если он правильно используется) и может легко справляться с большими картами. Я знаю о EnumMap
, который очень полезен для ключей перечисления, но часто я генерирую небольшую карту, которая никогда не будет очень большой, скорее всего, будет отброшена довольно скоро и не будет иметь проблем с concurrency.
Является ли HashMap<K,V>
слишком сложным для этих небольших, локальных и временных применений? Есть ли другая, простая реализация, которую я могу использовать в этих случаях?
Я думаю, что я ищу реализацию Map
, которая аналогична ArrayList
для List
. Он существует?
Добавлено позже после ответов:
Вот сценарий, где медленная, но очень простая реализация может быть лучше - когда у меня есть много, многие из этих Map
s. Предположим, например, что у меня есть миллион или около того этих маленьких маленьких карт, каждый из которых содержит несколько (обычно менее трех) записей. У меня низкая базовая ставка - возможно, я на самом деле не ссылаюсь на них, прежде чем их отбрасывают большую часть времени. Это все еще так, что HashMap
- лучший выбор для них?
Использование ресурсов - это больше, чем просто скорость - я хотел бы что-то, что не фрагментирует кучу много, и, например, сделать GCs долгое время.
Возможно, что HashMap
- правильный ответ, но это не случай преждевременной оптимизации (или, по крайней мере, это может быть не так).
Добавлено намного позже после некоторого раздумья:
Я решил скомпоновать собственный SmallMap
. Легко сделать один с AbstractMap
. Я также добавил несколько конструкторов, чтобы a SmallMap
можно было построить из существующего Map
.
По пути мне пришлось решить, как представить Entry
и реализовать SmallSet
для метода entrySet
.
Я многому научился при кодировании (и модульном тестировании этого) и хочу поделиться этим, если кто-то еще захочет этого. Он находится на github здесь.
Ответы
Ответ 1
В Java нет стандартной небольшой реализации Map
. HashMap
- одна из лучших и наиболее гибких реализаций Map
, и ее трудно превзойти. Однако в очень маленькой области требований - где использование кучи и скорость строительства имеют первостепенное значение - можно сделать лучше.
Я реализовал SmallCollections в GitHub, чтобы продемонстрировать, как это можно сделать. Мне бы хотелось, чтобы некоторые комментарии о том, удалось ли мне преуспеть. Я отнюдь не уверен, что у меня есть.
Хотя предлагаемые здесь ответы иногда были полезными, они, как правило, склонны неправильно понимать суть дела. В любом случае, ответ на мой собственный вопрос был, в конце концов, намного полезнее для меня, чем дать ему.
Вопрос здесь послужил своей цели, и именно поэтому я сам "ответил сам".
Ответ 2
Я думаю, что это преждевременная оптимизация. У вас проблемы с памятью? Проблемы с производительностью при создании слишком большого количества карт? Если нет, я думаю, что HashMap в порядке.
Кроме того, глядя на API, я не вижу ничего проще, чем HashMap
.
Если у вас возникла проблема, вы можете свернуть свою собственную реализацию карты, которая имеет очень простые внутренние элементы. Но я сомневаюсь, что вы сделали бы лучше, чем реализации Map Map, плюс у вас есть накладные расходы, чтобы убедиться, что ваш новый класс работает. В этом случае может возникнуть проблема с вашим дизайном.
Ответ 3
A HashMap - это, пожалуй, самая легкая и простая коллекция.
Иногда более эффективным решением является использование POJO. например если ваши ключи являются именами полей и/или ваши значения являются примитивами.
Ответ 4
HashMap - хороший выбор, потому что он предлагает средний случай O(1)
puts and gets. Он не гарантирует упорядочение, как, например, SortedMap-реализации (т.е. TreeMap O(log n)
puts and gets), но если у вас нет требований к порядку, то лучше HashMap.
Ответ 5
Я согласен с @hvgotcodes в том, что это преждевременная оптимизация, но все же хорошо знать все инструменты в панели инструментов.
Если вы делаете много итераций над тем, что находится на карте, LinkedHashMap обычно намного быстрее, чем HashMap, если у вас много потоков, работающих с Map одновременно, ConcurrentHashMap часто является лучший выбор. Я бы не стал беспокоиться о том, что реализация карты неэффективна для небольших наборов данных. Как правило, наоборот, неправильно построенная карта легко становится неэффективной с большими объемами данных, если у вас плохие хеш-значения или что-то заставляет ее иметь слишком мало ведер для ее загрузки.
Тогда, конечно, есть случаи, когда HashMap вообще не имеет смысла, например, если у вас есть три значения, которые вы всегда будете индексировать с помощью клавиш 0, 1 и 2, но я предполагаю, что вы понимаете, что: -)
Ответ 6
HashMap использует большую или меньшую память (при создании) в зависимости от того, как вы ее инициализируете: больше ведер означает больше использования памяти, но более быстрый доступ для больших количеств элементов; если вам нужно только небольшое количество элементов, вы можете инициализировать его с небольшим значением, которое будет производить меньше ведер, которые все равно будут быстрыми (так как каждый из них получит несколько элементов). Если вы правильно настроили его, нет никакой потери памяти (компромисс - это в основном использование памяти и скорость).
Что касается фрагментации кучи и траты GC-цикла, и многое другое, реализация Map может быть очень большой; все это возвращается к тому, как вы его устанавливаете. Поймите, что речь идет не о реализации Java, а о том, что общий (как, например, не может принимать что-либо о значениях ключа, таких как EnumMap
) hashtables (not HashTable
s), является наилучшим вариантом реализации структуры карты.
Ответ 7
Android имеет ArrayMap с целью сведения к минимуму памяти. Помимо того, что он находится в ядре, он находится в библиотеке поддержки v4, которая, теоретически, должна быть в состоянии скомпилировать для Oracle или OpenJDK JRE. Вот ссылка на источник ArrayMap в вилке библиотеки поддержки v4 на github.
Ответ 8
Существует альтернатива AirConcurrentMap, которая обладает большей эффективностью памяти выше 1K записей, чем любая другая карта, которую я нашел, и быстрее, чем ConcurrentSkipListMap для операций на основе ключей и быстрее, чем любая карта для итераций, и имеет внутренний пул потоков для параллельное сканирование. Это упорядоченная, то есть NavigableMap и ConcurrentMap. Он является бесплатным для некоммерческого использования без источника и коммерчески лицензируется с источником или без него. См. Catbay.com для графиков. Полное раскрытие: я автор.
AirConcurrentMap соответствует стандартам, поэтому он совместим с плагинами везде, даже для обычной Карты.
Итераторы уже очень быстрые, особенно за 1K Записи. При более высокоскоростном сканировании используется модель "посетитель" с обратным вызовом с одним посещением (k, v), который достигает скорости параллельных потоков Java 8. Параллельное сканирование AirConcurrentMap превышает параллельные потоки Java 8 примерно на 4 раза. Посетитель добавляет методы split() и merge() к однопоточному посетителю, которые напоминают карту map/reduce:
static class ThreadedSummingVisitor<K> extends ThreadedMapVisitor<K, Long> {
private long sum;
// This is idiomatic
long getSum(VisitableMap<K, Long> map) {
sum = 0;
map.getVisitable().visit(this);
return sum;
}
@Override
public void visit(Object k, Long v) {
sum += ((Long)v).longValue();
}
@Override
public ThreadedMapVisitor<K, Long> split() {
return new ThreadedSummingVisitor<K>();
}
@Override
public void merge(ThreadedMapVisitor<K, Long> visitor) {
sum += ((ThreadedSummingVisitor<K>)visitor).sum;
}
}
...
// The threaded summer can be re-used in one line now.
long sum = new ThreadedSummingVisitor().getSum((VisitableMap)map);