Как поясняется в обновлении 3 этого ответа , это обозначение:
var hash = {};
hash[X]
на самом деле не хэширует объект X
; на самом деле он просто конвертируется X
в строку (через, .toString()
если это объект, или некоторые другие встроенные преобразования для различных типов примитивов), а затем просматривает эту строку без хеширования в " hash
". Равенство объектов также не проверяется - если два разных объекта имеют одинаковое преобразование строк, они просто перезаписывают друг друга.
Учитывая это - есть ли эффективные реализации hashmaps в javascript? (Например, 2-й результат Google javascript hashmap
дает реализацию, которая является O (n) для любой операции. Различные другие результаты игнорируют тот факт, что разные объекты с эквивалентными строковыми представлениями перезаписывают друг друга.
Ответы:
Почему бы не хэшировать свои объекты вручную и не использовать полученные строки в качестве ключей для обычного словаря JavaScript? В конце концов, вы в лучшем положении, чтобы знать, что делает ваши объекты уникальными. Это то, чем я занимаюсь.
Пример:
Таким образом, вы можете контролировать индексирование, выполняемое JavaScript, без значительного увеличения выделения памяти и обработки переполнения.
Конечно, если вы действительно хотите «решение промышленного уровня», вы можете создать класс, параметризованный функцией ключа и со всем необходимым API контейнера, но… мы используем JavaScript и стараемся быть простыми и легкими, поэтому это функциональное решение просто и быстро.
Функция ключа может быть такой же простой, как выбор правильных атрибутов объекта, например ключа или набора ключей, которые уже являются уникальными, комбинации ключей, которые уникальны вместе, или такой сложной, как использование некоторых криптографических хешей, таких как в кодировке DojoX или UUID DojoX . Хотя последние решения могут создавать уникальные ключи, лично я стараюсь избегать их любой ценой, особенно если я знаю, что делает мои объекты уникальными.
Обновление в 2014 году. Ответ на это простое решение еще в 2008 году требует дополнительных пояснений. Позвольте мне прояснить идею в форме вопросов и ответов.
Ваше решение не имеет реального хэша. Где это находится???
JavaScript - это язык высокого уровня. Его базовый примитив ( Object ) включает в себя хеш-таблицу для сохранения свойств. Эта хеш-таблица обычно написана на языке низкого уровня для эффективности. Используя простой объект со строковыми ключами, мы используем эффективно реализованную хеш-таблицу без каких-либо усилий с нашей стороны.
Откуда вы знаете, что они используют хэш?
Существует три основных способа сохранить коллекцию объектов, адресуемых ключом:
Очевидно, объекты JavaScript используют хеш-таблицы в той или иной форме для обработки общих случаев.
Действительно ли производители браузеров используют хеш-таблицы ???
В самом деле.
Они справляются со столкновениями?
Да. Смотри выше. Если вы обнаружили коллизию на неравных строках, пожалуйста, не стесняйтесь сообщать об ошибке поставщику.
Так в чем твоя идея?
Если вы хотите хешировать объект, найдите, что делает его уникальным, и используйте его в качестве ключа. Не пытайтесь вычислить реальный хэш или эмулировать хеш-таблицы - он уже эффективно обрабатывается базовым объектом JavaScript.
Используйте этот ключ с JavaScript,
Object
чтобы использовать встроенную хэш-таблицу, избегая возможных конфликтов со свойствами по умолчанию.Примеры для начала:
Я использовал ваше предложение и кэшировал все объекты, используя имя пользователя. Но какого-то мудрого парня зовут «toString», который является встроенным свойством! Что мне теперь делать?
Очевидно, что если даже отдаленно возможно, что полученный ключ будет состоять исключительно из латинских символов, вы должны что-то с этим сделать. Например, добавьте любые нелатинские символы Юникода, которые вам нравятся в начале или в конце, чтобы снять конфликт со свойствами по умолчанию: "#toString", "#MarySmith". Если используется составной ключ, разделите ключевые компоненты, используя некий латинский разделитель: «имя, город, штат».
В общем, это место, где мы должны проявлять креативность и выбирать самые простые ключи с заданными ограничениями (уникальность, потенциальные конфликты со свойствами по умолчанию).
Примечание: уникальные ключи не конфликтуют по определению, в то время как потенциальные конфликты хешей будут обрабатываться базовым объектом
Object
.Почему вам не нравятся промышленные решения?
ИМХО, лучший код - это вовсе не код: он не содержит ошибок, не требует обслуживания, прост в понимании и выполняется мгновенно. Все «хеш-таблицы в JavaScript», которые я видел, были> 100 строками кода и включали несколько объектов. Сравните с:
dict[key] = value
.Еще один момент: возможно ли даже превзойти производительность первичного объекта, написанного на низкоуровневом языке, используя JavaScript и те же самые изначальные объекты для реализации того, что уже реализовано?
Я все еще хочу хешировать свои объекты без каких-либо ключей!
Нам повезло: ECMAScript 6 (выпуск которого запланирован на середину 2015 года, даст или возьмет 1-2 года после этого, чтобы стать широко распространенным) определяет карту и набор .
Судя по определению, они могут использовать адрес объекта в качестве ключа, что делает объекты мгновенно различимыми без искусственных ключей. OTOH, два разных, но одинаковых объекта, будут отображаться как отдельные.
Сравнительная разбивка по MDN :
источник
Описание проблемы
В JavaScript нет встроенного общего типа карты (иногда называемого ассоциативным массивом или словарем ), который позволяет получить доступ к произвольным значениям по произвольным ключам. Фундаментальная структура данных JavaScript - это объект , особый тип карты, который принимает только строки в качестве ключей и имеет особую семантику, такую как наследование прототипов, геттеры и сеттеры и некоторые другие вуду.
Когда объекты используются в качестве карт, вы должны помнить, что ключ будет преобразован в строковое значение через
toString()
, что приводит к отображению5
и'5'
тому же значению, а все объекты, которые не перезаписываютtoString()
метод, к индексируемому значению'[object Object]'
. Вы также можете невольно получить доступ к его унаследованным свойствам, если вы не проверитеhasOwnProperty()
.Встроенный в JavaScript тип массива ни на что не влияет: массивы JavaScript - это не ассоциативные массивы, а просто объекты с еще несколькими особыми свойствами. Если вы хотите знать, почему их нельзя использовать в качестве карт, посмотрите здесь .
Решение Евгения
Евгений Лазуткин уже описал основную идею использования пользовательской хеш-функции для генерации уникальных строк, которые можно использовать для поиска связанных значений как свойств объекта словаря. Скорее всего, это будет самое быстрое решение, поскольку объекты внутренне реализованы в виде хеш-таблиц .
Чтобы получить уникальное значение хеш-функции для произвольных объектов, одной из возможностей является использование глобального счетчика и кэширование значения хеш-функции в самом объекте (например, в названном свойстве
__hash
).Хэш-функция, которая делает это и работает как для примитивных значений, так и для объектов:
Эту функцию можно использовать как описано Евгением. Для удобства мы еще обернем его в
Map
классе.Моя
Map
реализацияСледующая реализация дополнительно сохранит пары ключ-значение в двусвязном списке, чтобы обеспечить быструю итерацию по ключам и значениям. Чтобы предоставить свою собственную хеш-функцию, вы можете переписать метод экземпляра
hash()
после создания.пример
Следующий скрипт
генерирует этот вывод:
Дальнейшие соображения
PEZ предложил перезаписать
toString()
метод, предположительно, нашей хэш-функцией. Это неосуществимо, потому что оно не работает для примитивных значений (изменениеtoString()
примитивов - очень плохая идея). Если мы хотимtoString()
вернуть значимые значения для произвольных объектов, нам придется изменитьObject.prototype
, что некоторые люди (включая меня) не считают верботенами .Изменить: текущую версию моей
Map
реализации, а также другие вкусности JavaScript можно получить здесь .источник
Map._counter = 0
и в конструкторе Map сделатьthis._hash = 'object ' + Map._counter++
. Тогда hash () становитсяreturn (value && value._hash) || (typeof(value) + ' ' + String(value));
Я знаю, что этот вопрос довольно старый, но сейчас есть несколько действительно хороших решений с внешними библиотеками.
У JavaScript также есть свой язык
Map
.источник
Вот простой и удобный способ использования чего-то похожего на карту Java:
И чтобы получить значение:
источник
В соответствии со стандартом ECMAScript 2015 (ES6) JavaScript имеет реализацию Map. Подробнее о том, что можно найти здесь
Основное использование:
источник
Вы можете использовать ES6
WeakMap
илиMap
:Имейте в виду, что ни одна из них широко не поддерживается, но вы можете использовать ES6 Shim (требуется собственная ES5 или ES5 Shim ) для поддержки
Map
, но неWeakMap
( смотрите почему ).источник
Вы должны хранить в некоторых внутренних состояниях парные пары объект / значение
И используйте его как таковой:
Конечно, эта реализация также находится где-то на уровне O (n). Приведенные выше примеры Юджина - единственный способ получить хеш, который работает с любой скоростью, которую вы ожидаете получить от реального хеша.
Обновить:
Другой подход, аналогичный ответу Евгения, заключается в том, чтобы каким-то образом прикрепить уникальный идентификатор ко всем объектам. Один из моих любимых подходов состоит в том, чтобы взять один из встроенных методов, унаследованных от суперкласса Object, заменить его на выборку пользовательской функции и прикрепить свойства к этому объекту функции. Если бы вы переписали мой метод HashMap для этого, он бы выглядел так:
Эта версия, кажется, только немного быстрее, но теоретически она будет значительно быстрее для больших наборов данных.
источник
К сожалению, ни один из приведенных выше ответов не подходит для моего случая: разные ключевые объекты могут иметь одинаковый хэш-код. Поэтому я написал простую Java-подобную версию HashMap:
Примечание: ключевые объекты должны «реализовывать» методы hashCode () и equals ().
источник
new Array()
over[]
заключается в том, чтобы обеспечить абсолютное сходство Java вашего кода? :)Я реализовал JavaScript HashMap, код которого можно получить из адресу http://github.com/lambder/HashMapJS/tree/master
Вот код:
источник
HashMap
s.В ECMA6 вы можете использовать WeakMap
Пример:
Но:
источник
Javascript не встроен в карту / hashmap. Это следует называть ассоциативным массивом .
hash["X"]
равноhash.X
, но разрешить "X" в качестве строковой переменной. Другими словами,hash[x]
функционально равноeval("hash."+x.toString())
Это больше похоже на object.properties, чем на сопоставление ключ-значение. Если вы ищете лучшее отображение ключа / значения в Javascript, используйте объект Map, который вы можете найти в Интернете.
источник
Попробуйте мою реализацию хэш-таблицы JavaScript: http://www.timdown.co.uk/jshashtable
Он ищет метод ключевых объектов hashCode (), или вы можете предоставить функцию хеширования при создании объекта Hashtable.
источник
Это выглядит как довольно надежное решение: https://github.com/flesler/hashmap . Это будет даже хорошо работать для функций и объектов, которые выглядят одинаково. Единственный хак, который он использует, - это добавление неясного члена к объекту для его идентификации. Если ваша программа не перезаписывает эту непонятную переменную (это что-то вроде хеш-кода ), вы великолепны .
источник
Если производительность не важна (например , количество ключей относительно невелико) , и вы не хотите , чтобы загрязнить ваш (или , возможно , не ваш) объекты с дополнительными полями , как
_hash
,_id
и т.д., то вы можете использовать тот факт , чтоArray.prototype.indexOf
использует строгое равенство. Вот простая реализация:Пример использования:
По сравнению с ES6 WeakMap у него есть две проблемы: O (n) время поиска и отсутствие уязвимостей (т.е. это приведет к утечке памяти, если вы не используете
delete
или неclear
отпускаете ключи).источник
Моя реализация карты, полученная из примера Кристофа:
Пример использования:
источник
Добавление еще одного решения:
HashMap
это почти первый класс, который я перенес с Java на Javascript. Можно сказать, что накладных расходов много, но реализация почти на 100% равна реализации Java и включает все интерфейсы и подклассы.Проект можно найти здесь: https://github.com/Airblader/jsava Я также приложу (текущий) исходный код для класса HashMap, но, как указано, он также зависит от суперкласса и т. Д. Используемая OOP-инфраструктура это qooxdoo.
Изменить: Обратите внимание, что этот код уже устарел и обратитесь к проекту GitHub для текущей работы. На момент написания этого, есть также
ArrayList
реализация.источник
Еще одна реализация карты мной. С рандомизатором, 'generics' и 'iterator' =)
Пример:
источник