Коллекция ключей Java со сложностью O (1) для миллионов случайных неупорядоченных ключей

Я столкнулся с проблемой, когда у меня есть миллионы пар ключ-значение, к которым мне нужно получить доступ, используя ключи случайным образом (не с помощью итератора).

Диапазон ключей неизвестен во время компиляции, но известно общее количество пар ключ-значение.

Я просмотрел структуры данных HashMap и Hashset, но они на самом деле не O(1), так как в случае коллизии в хэш-коде они стать массивом LinkedLists, который в худшем случае имеет линейную сложность поиска.

Я также рассматривал возможность увеличения количества сегментов в HashMap, но это не гарантирует, что каждый элемент будет храниться в отдельном сегменте.

Есть ли способ хранить и получать доступ к миллионам пар ключ-значение со сложностью O(1)?

В идеале я хотел бы, чтобы каждый ключ был похож на переменную, и соответствующее значение должно быть значением, присвоенным этому ключу.

Заранее спасибо.


person Kami    schedule 13.02.2014    source источник
comment
Есть ли у вас какое-либо доказательство, например теоретико-информационное обоснование или измерения, того, что ваши данные вызывают столько коллизий, что время поиска больше не составляет O(1)? Если нет, то вы зря беспокоитесь.   -  person    schedule 13.02.2014
comment
можете ли вы попробовать использовать хэш ключа и значения вместе и сохранить его как ключ в хеш-таблице, я думаю, это решит вашу проблему.   -  person Nachiket Kate    schedule 13.02.2014
comment
@Nachiket Это одновременно противоречит цели коллекции ключ-значение (невозможно найти значение по ключу, нужно знать его, чтобы найти его), и вводит больше возможностей для коллизий хэшей, а не меньше.   -  person    schedule 13.02.2014
comment
@delnan с точки зрения доказательств, я знаю, что будут миллионы значений в диапазоне от 1 до нескольких миллиардов, и они будут встречаться случайным образом, поэтому я не могу написать хорошую функцию Hash, что означает, что я не знаю сколько ключей будет хешировано в сегмент   -  person Kami    schedule 13.02.2014
comment
в Java 8 HashMap будет использовать какое-то сбалансированное дерево вместо списков для обработки коллизий. Также попробуйте с лучшей хеш-функцией. openjdk.java.net/jeps/180   -  person Svetlin Zarev    schedule 13.02.2014
comment
Я согласен с частью хеширования... кажется, что реализация Hash в гуаве делает свое дело - никогда не пробовал: docs.guava-libraries.googlecode.com/git-history/v14.0/javadoc/   -  person Olimpiu POP    schedule 13.02.2014
comment
@Ками Это не имеет смысла. Ничто из того, что вы говорите, не намекает на более высокую, чем ожидалось, вероятность коллизии, и написание хороших хеш-функций в любом случае в основном не зависит от этих проблем (поскольку вы хотите, чтобы они хорошо работали для всех видов данных, а не знать много о ключах на самом деле по умолчанию). Просто, блин, попробуй. Держу пари, это будет работать просто отлично?   -  person    schedule 13.02.2014
comment
Я все еще думаю, что вам следует подумать над тем, что сказал @delnan. Действительно ли вы наблюдаете временную сложность ваших данных, отличную от O(1)? Для размеров данных, намного превышающих 4 294 967 296, решением будет реализация Hashtable с использованием длинных хэшей вместо хэшей int. Это позволит вашему набору данных достигать 18 446 744 073 709 551 616 с низкой вероятностью коллизий.   -  person Axoren    schedule 13.02.2014
comment
@delnan Я попробую с хэш-функцией по умолчанию и сообщу вам, ребята, о результатах ... большое спасибо всем за вашу быструю помощь.   -  person Kami    schedule 13.02.2014
comment
@Kami Вы, скорее всего, увидите замедление из-за того, что ваши данные больше не помещаются в кеши ЦП. Случайный доступ к основной памяти может быть в 50 раз медленнее, чем доступ к памяти в кеше L1. Для сравнения, я ожидаю, что один миллион ключей будет примерно в 1,6 раза медленнее при разумных предположениях о частоте столкновений. Кстати, HashMap не использует LinkedList.   -  person Peter Lawrey    schedule 13.02.2014
comment
@PeterLawrey Кэш-промахи повсюду, хэш-карта с миллионом элементов не намного чаще вызывает их, чем обычный доступ к объектам, хэш-таблица из тысячи элементов или случайный доступ к массиву из тысячи элементов, чтобы привести несколько примеров. Более того, часто удивительно сложно предсказать масштабы этих эффектов и еще сложнее улучшить их без радикальной перестройки алгоритма. Это все равно, что сказать архитектору, чтобы он беспокоился о детях, разрисовывающих граффити, а не о завершении строительства.   -  person    schedule 13.02.2014
comment
@PeterLawrey ТАКЖЕ, HashMap использует связанный список для сегментов, по крайней мере, в любой версии, которую я проверял. Он не использует java.util.LinkedList, но это мало что говорит.   -  person    schedule 13.02.2014


Ответы (2)


Я думаю, вы путаете, что представляет собой нотация Big O. Он определяет ограничивающее поведение функции, не обязательно фактическое поведение.

Средняя сложность хеш-карты составляет O(1) для операций вставки, удаления и поиска. Что это значит? В среднем эти операции будут выполняться за постоянное время независимо от размера хеш-карты. Таким образом, в зависимости от реализации карты, поиск может занять не ровно один шаг, но, скорее всего, он не будет включать более нескольких шагов относительно размера хэш-карты.

Насколько хорошо хеш-карта ведет себя для этих операций, определяется несколькими факторами. Наиболее очевидным является хэш-функция, используемая для группировки ключей. Предпочтительны хэш-функции, которые более равномерно распределяют вычисленные хэши по диапазону хэшей и ограничивают количество коллизий. Чем лучше хеш-функция в этих областях, тем ближе хеш-карта будет работать за постоянное время.

Другим фактором, влияющим на фактическое поведение хеш-карты, является способ управления хранилищем. То, как карта изменяет размеры и перемещает записи по мере добавления и удаления элементов, помогает контролировать коллизии хэшей, используя оптимальное количество сегментов. Эффективное управление хранилищем хэш-карты позволит хэш-карте работать почти постоянно.

С учетом всего сказанного существуют способы построения хэш-карт, которые имеют поведение O (1) в наихудшем случае для поиска. Это достигается с помощью идеальной хеш-функции. Совершенная хэш-функция — это обратимая функция 1-1 между ключами и хэшами. С идеальной хэш-функцией и надлежащим хранилищем хэш-карт можно добиться O (1) поиска. Предпосылкой для использования этого подхода является знание всех значений ключей заранее, чтобы можно было разработать идеальную хеш-функцию.

К сожалению, в вашем случае не используются известные ключи, поэтому идеальную хэш-функцию построить невозможно, но имеющиеся исследования могут помочь вам построить почти идеальную хеш-функцию для вашего случая.

person Brent Worden    schedule 13.02.2014
comment
Будет ли это полезно, если я скажу, что использую Integer в качестве ключей, а диапазон будет от 1 до нескольких миллиардов, но в какой-то момент HashMap будет иметь несколько миллионов случайных целых чисел из этого диапазона как ключи - person Kami; 13.02.2014

Нет, такой (известной) структуры данных для универсальных типов данных не существует.

Если бы это было так, он, скорее всего, заменил бы хеш-таблицы в наиболее часто используемых библиотеках, если только нет какого-то существенного недостатка, такого как массивный постоянный фактор или нелепое использование памяти, что, вероятно, сделало бы его нежизнеспособным и для вас.

Я сказал «общие типы данных» выше, так как могут быть некоторые особые случаи, для которых это возможно, например, когда ключ является целым числом в небольшом диапазоне — в этом случае вы можете просто иметь массив, где каждый индекс соответствует тот же ключ, но на самом деле это также хеш-таблица, в которой ключ хеширует сам себя.


Обратите внимание, что вам нужна ужасная хеш-функция, патологический ввод для вашего хэша. функцию или очень маленькую хеш-таблицу, чтобы получить наихудшую производительность O (n) для вашей хеш-таблицы. Вы действительно должны протестировать его и посмотреть, достаточно ли он быстр, прежде чем искать что-то еще. Вы также можете попробовать TreeMap, который с его операциями O(log n) иногда превосходит HashMap.

person Bernhard Barker    schedule 13.02.2014