Como ficou claro na atualização 3 desta resposta , esta notação:
var hash = {};
hash[X]
na verdade não faz o hash do objeto X
; na verdade, apenas converte X
em uma sequência de caracteres (via .toString()
se for um objeto ou em algumas outras conversões internas para vários tipos primitivos) e, em seguida, procura essa sequência, sem hash, em " hash
". A igualdade de objetos também não é verificada - se dois objetos diferentes tiverem a mesma conversão de cadeia, eles serão substituídos.
Diante disso - existem implementações eficientes de hashmaps em javascript? (Por exemplo, o segundo resultado do Google javascript hashmap
produz uma implementação que é O (n) para qualquer operação. Vários outros resultados ignoram o fato de que objetos diferentes com representações de sequência equivalentes se sobrescrevem.
Respostas:
Por que não manipular seus objetos manualmente e usar as seqüências resultantes como chaves para um dicionário JavaScript comum? Afinal, você está na melhor posição para saber o que torna seus objetos únicos. Isto é o que eu faço.
Exemplo:
Dessa forma, você pode controlar a indexação feita por JavaScript sem sobrecarregar a alocação de memória e manipular o estouro.
Obviamente, se você realmente deseja a "solução de nível industrial", pode criar uma classe parametrizada pela função-chave e com toda a API necessária do contêiner, mas ... usamos JavaScript e tentamos ser simples e leves, então Esta solução funcional é simples e rápida.
A função de chave pode ser tão simples quanto selecionar os atributos corretos do objeto, por exemplo, uma chave ou um conjunto de chaves que já são únicas, uma combinação de chaves que são únicas juntas ou tão complexas quanto o uso de alguns hashes criptográficos, como na codificação DojoX , ou DojoX UUID . Embora as últimas soluções possam produzir chaves exclusivas, pessoalmente tento evitá-las a todo custo, principalmente se souber o que torna meus objetos únicos.
Atualização em 2014: respondida em 2008, esta solução simples ainda requer mais explicações. Deixe-me esclarecer a ideia em um formulário de perguntas e respostas.
Sua solução não possui um hash real. Cadê???
JavaScript é uma linguagem de alto nível. Sua primitiva básica ( Object ) inclui uma tabela de hash para manter as propriedades. Essa tabela de hash geralmente é escrita em um idioma de baixo nível para eficiência. Usando um objeto simples com chaves de string, usamos uma tabela de hash implementada com eficiência, sem nenhum esforço de nossa parte.
Como você sabe que eles usam hash?
Existem três maneiras principais de manter uma coleção de objetos endereçáveis por uma chave:
Obviamente, os objetos JavaScript usam tabelas de hash de alguma forma para lidar com casos gerais.
Os fornecedores de navegadores realmente usam tabelas de hash ???
Realmente.
Eles lidam com colisões?
Sim. Veja acima. Se você encontrou uma colisão em cadeias desiguais, não hesite em registrar um bug com um fornecedor.
Então, qual é a sua ideia?
Se você deseja fazer o hash de um objeto, encontre o que o torna único e use-o como chave. Não tente calcular o hash real ou emular tabelas de hash - ele já é tratado com eficiência pelo objeto JavaScript subjacente.
Use essa chave com JavaScript
Object
para alavancar sua tabela de hash integrada enquanto evita possíveis conflitos com propriedades padrão.Exemplos para você começar:
Usei sua sugestão e coloquei em cache todos os objetos usando um nome de usuário. Mas um cara sábio se chama "toString", que é uma propriedade interna! O que eu deveria fazer agora?
Obviamente, se é possível remotamente que a chave resultante consista exclusivamente em caracteres latinos, você deve fazer algo a respeito. Por exemplo, adicione qualquer caractere Unicode não latino que você goste no início ou no final para desestabilizar as propriedades padrão: "#toString", "#MarySmith". Se uma chave composta for usada, separe os componentes-chave usando algum tipo de delimitador não latino: "nome, cidade, estado".
Em geral, este é o local em que precisamos ser criativos e selecionar as chaves mais fáceis com limitações dadas (exclusividade, possíveis conflitos com propriedades padrão).
Nota: as chaves exclusivas não se chocam por definição, enquanto os possíveis confrontos de hash serão tratados pelo subjacente
Object
.Por que você não gosta de soluções industriais?
IMHO, o melhor código não é código: não possui erros, não requer manutenção, é fácil de entender e é executado instantaneamente. Todas as "tabelas de hash em JavaScript" que vi eram> 100 linhas de código e envolviam vários objetos. Compará-lo com:
dict[key] = value
.Outro ponto: é possível vencer uma performance de um objeto primordial escrito em uma linguagem de baixo nível, usando JavaScript e os mesmos objetos primordiais para implementar o que já está implementado?
Eu ainda quero misturar meus objetos sem nenhuma chave!
Estamos com sorte: o ECMAScript 6 (agendado para lançamento em meados de 2015, mais ou menos 1 a 2 anos depois disso para se espalhar) define mapa e cenário .
A julgar pela definição, eles podem usar o endereço do objeto como uma chave, o que torna os objetos instantaneamente distintos sem chaves artificiais. OTOH, dois objetos diferentes, mas idênticos, serão mapeados como distintos.
Análise comparativa do MDN :
fonte
Descrição do Problema
O JavaScript não possui um tipo de mapa geral embutido (às vezes chamado de array ou dicionário associativo ) que permite acessar valores arbitrários por chaves arbitrárias. A estrutura de dados fundamental do JavaScript é o objeto , um tipo especial de mapa que aceita apenas cadeias de caracteres como chaves e possui semântica especial como herança prototípica, getters e setters e mais algum vodu.
Ao usar objetos como mapas, lembre-se de que a chave será convertida em um valor de string via
toString()
, o que resulta no mapeamento5
e'5'
no mesmo valor e em todos os objetos que não substituem otoString()
método pelo valor indexado por'[object Object]'
. Você também pode acessar involuntariamente suas propriedades herdadas se não marcarhasOwnProperty()
.O tipo de matriz interno do JavaScript não ajuda em nada: as matrizes JavaScript não são matrizes associativas, mas apenas objetos com mais algumas propriedades especiais. Se você quiser saber por que eles não podem ser usados como mapas, veja aqui .
Solução de Eugene
Eugene Lazutkin já descreveu a idéia básica de usar uma função hash customizada para gerar strings exclusivas que podem ser usadas para procurar os valores associados como propriedades de um objeto de dicionário. Essa provavelmente será a solução mais rápida, porque os objetos são implementados internamente como tabelas de hash .
Para obter um valor de hash exclusivo para objetos arbitrários, uma possibilidade é usar um contador global e armazenar em cache o valor de hash no próprio objeto (por exemplo, em uma propriedade chamada
__hash
).Uma função hash que faz isso e funciona para valores e objetos primitivos é:
Esta função pode ser usada como descrito por Eugene. Por conveniência, vamos envolvê-lo ainda mais em uma
Map
classe.Minha
Map
implementaçãoA implementação a seguir armazenará adicionalmente os pares de valores-chave em uma lista duplamente vinculada para permitir uma iteração rápida sobre chaves e valores. Para fornecer sua própria função de hash, você pode substituir o
hash()
método da instância após a criação.Exemplo
O script a seguir
gera esta saída:
Considerações adicionais
O PEZ sugeriu sobrescrever o
toString()
método, presumivelmente com a nossa função hash. Esta não é viável porque ele não funciona para valores primitivos (mudandotoString()
para primitivas é uma muito má ideia). Se quisermostoString()
retornar valores significativos para objetos arbitrários, teríamos que modificarObject.prototype
, o que algumas pessoas (inclusive eu) não consideram verboten .Edit: A versão atual da minha
Map
implementação, bem como outras vantagens do JavaScript, podem ser obtidas aqui .fonte
Map._counter = 0
, e no construtor Map, façathis._hash = 'object ' + Map._counter++
. Então, o hash () se tornareturn (value && value._hash) || (typeof(value) + ' ' + String(value));
Eu sei que essa pergunta é bastante antiga, mas existem algumas ótimas soluções hoje em dia com bibliotecas externas.
O JavaScript também possui sua linguagem fornecida
Map
.fonte
Aqui está uma maneira fácil e conveniente de usar algo semelhante ao mapa java:
E para obter o valor:
fonte
De acordo com o padrão ECMAScript 2015 (ES6), o javascript possui uma implementação de Mapa. Mais sobre o que pode ser encontrado aqui
Uso básico:
fonte
Você pode usar o ES6
WeakMap
ouMap
:Esteja ciente de que nenhum deles é amplamente suportado, mas você pode usar o ES6 Shim (requer ES5 nativo ou ES5 Shim ) para dar suporte
Map
, mas nãoWeakMap
( veja o porquê ).fonte
Você precisaria armazenar em alguns dísticos de estado interno dos pares objeto / valor
E use-o como tal:
Obviamente, essa implementação também está em algum lugar na linha de O (n). Os exemplos de Eugene acima são a única maneira de obter um hash que funcione com qualquer velocidade que você esperaria de um hash real.
Atualizar:
Outra abordagem, na linha da resposta de Eugene, é de alguma forma anexar um ID exclusivo a todos os objetos. Uma das minhas abordagens favoritas é usar um dos métodos internos herdados da superclasse Object, substituí-lo por uma passagem de função personalizada e anexar propriedades a esse objeto de função. Se você reescrevesse meu método HashMap para fazer isso, seria semelhante a:
Essa versão parece ser apenas um pouco mais rápida, mas em teoria será significativamente mais rápida para grandes conjuntos de dados.
fonte
Infelizmente, nenhuma das respostas acima foi boa para o meu caso: objetos-chave diferentes podem ter o mesmo código de hash. Portanto, escrevi uma versão simples do HashMap semelhante a Java:
Nota: os objetos principais devem "implementar" os métodos hashCode () e equals ().
fonte
new Array()
over[]
é garantir a absoluta semelhança com Java do seu código? :)Eu implementei um HashMap JavaScript, cujo código pode ser obtido em http://github.com/lambder/HashMapJS/tree/master
Aqui está o código:
fonte
HashMap
s.No ECMA6, você pode usar o WeakMap
Exemplo:
Mas:
fonte
Javascript não constrói Mapa / hashmap. Deve ser chamado de matriz associativa .
hash["X"]
é igual ahash.X
, mas permite "X" como uma variável de sequência. Em outras palavras,hash[x]
é funcionalmente igual aeval("hash."+x.toString())
É mais semelhante a object.properties, em vez de mapeamento de valores-chave. Se você estiver procurando um melhor mapeamento de chave / valor em Javascript, use o objeto Map que pode ser encontrado na web.
fonte
Tente minha implementação da tabela de hash JavaScript: http://www.timdown.co.uk/jshashtable
Ele procura um método hashCode () dos objetos principais, ou você pode fornecer uma função de hash ao criar um objeto Hashtable.
fonte
Parece uma solução bastante robusta: https://github.com/flesler/hashmap . Até funcionará bem para funções e objetos que parecem idênticos. O único truque usado é adicionar um membro obscuro a um objeto para identificá-lo. Se o seu programa não sobrescrever essa variável obscura (é algo como hashid ), você estará em ouro.
fonte
Se o desempenho não é crítico (por exemplo, a quantidade de chaves é relativamente pequeno) e você não quer poluir o seu (ou talvez não a sua) objetos com campos adicionais, como
_hash
,_id
, etc., então você pode fazer uso do fato de queArray.prototype.indexOf
emprega igualdade estrita. Aqui está uma implementação simples:Exemplo de uso:
Comparando com o ES6 WeakMap, há dois problemas: O (n) tempo de pesquisa e não fraqueza (ou seja, causará vazamento de memória se você não usar
delete
ouclear
liberar chaves).fonte
My Map Implementation, derivado do exemplo de Christoph:
Exemplo de uso:
fonte
Adicionando mais uma solução:
HashMap
é praticamente a primeira classe que eu movi de Java para Javascript. Você poderia dizer que há muita sobrecarga, mas a implementação é quase 100% igual à implementação do Java e inclui todas as interfaces e subclasses.O projeto pode ser encontrado aqui: https://github.com/Airblader/jsava Também anexarei o código-fonte (atual) da classe HashMap, mas, como afirmado, também depende da superclasse etc. A estrutura OOP usada é qooxdoo.
Edit: Observe que este código já está desatualizado e consulte o projeto github para o trabalho atual. No momento em que escrevemos isso, também há uma
ArrayList
implementação.fonte
Mais uma implementação de mapa por mim. Com o randomizador, 'genéricos' e 'iterador' =)
Exemplo:
fonte