Escuché en mis clases de grado que HashTable
colocará una nueva entrada en el grupo "siguiente disponible" si la nueva entrada clave choca con otra.
¿Cómo HashTable
devolvería el valor correcto si esta colisión se produce al llamar a uno con la tecla de colisión?
Supongo que Keys
son de String
tipo y hashCode()
devuelve el valor predeterminado generado por, por ejemplo, Java.
Si implemento mi propia función hash y la uso como parte de una tabla de búsqueda (es decir, un HashMap
o Dictionary
), ¿qué estrategias existen para hacer frente a las colisiones?
¡Incluso he visto notas relacionadas con números primos! Información no tan clara en la búsqueda de Google.
Cuando hablaste de "La tabla hash colocará una nueva entrada en el depósito 'siguiente disponible' si la nueva entrada clave choca con otra", estás hablando de la estrategia de direccionamiento abierto de resolución de colisión de la tabla hash.
Existen varias estrategias para que la tabla hash resuelva una colisión.
El primer tipo de método grande requiere que las claves (o punteros a ellas) se almacenen en la tabla, junto con los valores asociados, que además incluye:
Otro método importante para manejar la colisión es el cambio de tamaño dinámico , que además tiene varias formas:
EDITAR : lo anterior se tomó prestado de wiki_hash_table , donde debe ir a echar un vistazo para obtener más información.
fuente
Hay varias técnicas disponibles para manejar una colisión. Te explicare algunos de ellos
Encadenamiento: en el encadenamiento usamos índices de matriz para almacenar los valores. Si el código hash del segundo valor también apunta al mismo índice, entonces reemplazamos ese valor de índice con una lista vinculada y todos los valores que apuntan a ese índice se almacenan en la lista vinculada y el índice de la matriz real apunta al encabezado de la lista vinculada. Pero si solo hay un código hash que apunta a un índice de matriz, el valor se almacena directamente en ese índice. Se aplica la misma lógica al recuperar los valores. Esto se usa en Java HashMap / Hashtable para evitar colisiones.
Sondeo lineal: esta técnica se utiliza cuando tenemos más índice en la tabla que los valores a almacenar. La técnica de palpado lineal funciona con el concepto de seguir aumentando hasta encontrar una ranura vacía. El pseudocódigo se ve así:
Técnica de hash doble: en esta técnica usamos dos funciones de hash h1 (k) y h2 (k). Si la ranura en h1 (k) está ocupada, entonces la segunda función hash h2 (k) se usa para incrementar el índice. El pseudocódigo se ve así:
Las técnicas de sondeo lineal y doble hash son parte de la técnica de direccionamiento abierto y solo se pueden usar si las ranuras disponibles son más que la cantidad de elementos que se agregarán. Se necesita menos memoria que el encadenamiento porque no se usa ninguna estructura adicional aquí, pero es lento debido a que ocurren muchos movimientos hasta que encontramos un espacio vacío. También en la técnica de direccionamiento abierto, cuando un elemento se quita de una ranura, colocamos una lápida para indicar que el elemento se quita de aquí y por eso está vacío.
Para obtener más información, consulte este sitio .
fuente
Le sugiero que lea esta publicación de blog que apareció recientemente en HackerNews: Cómo funciona HashMap en Java
En resumen, la respuesta es
fuente
En realidad, esto no es cierto, al menos para Oracle JDK ( es un detalle de implementación que podría variar entre diferentes implementaciones de la API). En cambio, cada segmento contiene una lista vinculada de entradas anteriores a Java 8 y un árbol equilibrado en Java 8 o superior.
Utiliza el
equals()
para encontrar la entrada que realmente coincide.Existen varias estrategias de manejo de colisiones con diferentes ventajas y desventajas. La entrada de Wikipedia sobre tablas hash ofrece una buena descripción general.
fuente
Hashtable
yHashMap
en jdk 1.6.0_22 de Sun / Oracle.public V get(Object key)
este momento (la misma versión que la anterior). Si encuentra una versión precisa donde aparecen esas listas vinculadas, me interesaría saberlo.Entry
objetos:localEntry = localEntry.next
e = e.next
no lo es++index
. +1Actualización desde Java 8: Java 8 utiliza un árbol autoequilibrado para el manejo de colisiones, mejorando el peor de los casos de O (n) a O (log n) para la búsqueda. El uso de un árbol autoequilibrado se introdujo en Java 8 como una mejora sobre el encadenamiento (usado hasta Java 7), que usa una lista vinculada y tiene el peor caso de O (n) para la búsqueda (ya que necesita atravesar la lista)
Para responder a la segunda parte de su pregunta, la inserción se realiza mapeando un elemento dado a un índice dado en la matriz subyacente del mapa hash; sin embargo, cuando ocurre una colisión, todos los elementos deben conservarse (almacenados en una estructura de datos secundaria , y no solo reemplazado en la matriz subyacente). Esto generalmente se hace haciendo que cada componente de matriz (ranura) sea una estructura de datos secundaria (también conocida como depósito), y el elemento se agrega al depósito que reside en el índice de matriz dado (si la clave no existe ya en el depósito, en cuyo caso se reemplaza).
Durante la búsqueda, la clave se codifica con el índice de matriz correspondiente y se realiza la búsqueda de un elemento que coincida con la clave (exacta) en el depósito dado. Debido a que el bucket no necesita manejar colisiones (compara claves directamente), esto resuelve el problema de las colisiones, pero lo hace a costa de tener que realizar la inserción y búsqueda en la estructura de datos secundaria. El punto clave es que en un mapa hash, tanto la clave como el valor se almacenan, por lo que incluso si el hash choca, las claves se comparan directamente para determinar la igualdad (en el depósito) y, por lo tanto, se pueden identificar de forma única en el depósito.
El manejo de colisiones trae el peor de los casos de inserción y búsqueda de O (1) en el caso de no manejo de colisiones a O (n) para encadenamiento (una lista vinculada se usa como estructura de datos secundaria) y O (log n) para árbol autoequilibrado.
Referencias:
fuente
Utilizará el método equals para ver si la clave está presente incluso y especialmente si hay más de un elemento en el mismo depósito.
fuente
Como existe cierta confusión sobre qué algoritmo está usando Java HashMap (en la implementación de Sun / Oracle / OpenJDK), aquí los fragmentos de código fuente relevantes (de OpenJDK, 1.6.0_20, en Ubuntu):
Este método (la cita es de las líneas 355 a 371) se llama al buscar una entrada en la tabla, por ejemplo
get()
, de ,containsKey()
y algunas otras. El bucle for aquí pasa por la lista vinculada formada por los objetos de entrada.Aquí el código para los objetos de entrada (líneas 691-705 + 759):
Inmediatamente después de esto viene el
addEntry()
método:Esto agrega la nueva entrada en la parte delantera del cubo, con un enlace al antiguo primero entrada anterior (o nula, si no existe). De manera similar, el
removeEntryForKey()
método recorre la lista y se encarga de eliminar solo una entrada, dejando intacto el resto de la lista.Entonces, aquí hay una lista de entrada vinculada para cada segmento, y dudo mucho que esto haya cambiado de
_20
a_22
, ya que fue así desde 1.2 en adelante.(Este código es (c) 1997-2007 Sun Microsystems, y está disponible bajo GPL, pero para copiar mejor use el archivo original, contenido en src.zip en cada JDK de Sun / Oracle, y también en OpenJDK.)
fuente
aquí hay una implementación de tabla hash muy simple en java. en solo implementos
put()
yget()
, pero puede agregar fácilmente lo que quiera. se basa en elhashCode()
método de Java que es implementado por todos los objetos. podría crear fácilmente su propia interfaz,y obligarlo a que lo implementen las claves si lo desea.
fuente
Existen varios métodos para la resolución de colisiones, algunos de ellos son el encadenamiento separado, el direccionamiento abierto, el hash de Robin Hood, el hash de cuco, etc.
Java usa Separate Chaining para resolver colisiones en tablas Hash. Aquí hay un gran enlace a cómo sucede: http://javapapers.com/core-java/java-hashtable/
fuente