2009-09-23 16 views
19

¿Hay alguna razón por la cual una cadena Java no pueda ser probada para la igualdad usando su método hashCode? Así que, básicamente, en lugar de ....Probando la igualdad de cadenas usando hashCode()

"hello".equals("hello") 

Usted podría utilizar ...

"hello".hashCode() == "hello".hashCode() 

Esto sería útil porque una vez que una cadena se ha calculado que es código hash a continuación la comparación de una cadena sería tan eficiente como comparar un int como la cadena almacena en caché el hashcode y es bastante probable que la cadena esté en el grupo de cadenas de todos modos, si lo diseñó de esa manera.

+0

Su respuesta se encuentra en la documentación de los métodos 'equals()' y 'hashcode()'. – skaffman

+8

Y con respecto a la eficiencia: observe de cerca las cadenas que está comparando. Estaré dispuesto a apostar que> 50% difieren en su primer personaje, y> 66% difieren en sus primeros dos personajes. Entonces realmente tienes una comparación muy eficiente versus hashCode(), que tiene que recorrer toda la cadena. Además, si usa cadenas en el conjunto constante, equals() primero verifica la identidad, lo que los eliminará de inmediato. – kdgregory

+0

@kdgregory Es cierto que el peor de los casos es que las cadenas realmente coincidan, lo que también podría ser el escenario más común. También has respondido a mi pregunta subyacente sobre el rendimiento de la secuencia, cualquier ganancia que haya hecho un código hash agrupado ya se está realizando al realizar una verificación de instancia. ¡Aclamaciones! –

Respuesta

35

porque: los hashCodes de dos objetos deben ser iguales si los objetos son iguales; sin embargo, si dos objetos son desiguales, el código hash aún puede ser igual.

(modificado según comentario)

+5

+1, los códigos hash no garantizan la exclusividad: solo intentan proporcionar colisiones bajas – orip

+4

+1, sin embargo, una pequeña enmienda. Los hashCodes de dos objetos DEBEN ser iguales si los objetos son iguales, esto se especifica en el contrato de hashCode. – Falaina

+2

Respuesta breve y correcta.Hacer algo así solo para ser tan eficiente como sea posible me suena un poco como una optimización prematura. – NickDK

1

El valor hashCode no es único, lo que significa que las cadenas no puede llegar a igualar. Para mejorar el rendimiento, a menudo las implementaciones de iguales realizarán una comprobación de hashCode antes de realizar comprobaciones más laboriosas.

-2

No hay ninguna razón para no usar hashCode como usted lo describe.

Sin embargo, debe tener en cuenta las colisiones. Existe la posibilidad, una pequeña posibilidad, de que dos cadenas diferentes hagan hash con el mismo valor. Considera hacer un hashCode al principio, y si es igual también haz la comparación completa usando los iguales().

+0

¿En otras palabras, hay una razón para no usar hashcode como él describe? –

+0

así que no hay ninguna razón para descartarlo, solo una advertencia para hacer comprobaciones adicionales en algunas circunstancias – Will

+1

Entonces, si todavía tiene que hacer la prueba de igual a igual, ¿para qué molestarse? Acabas de escribir el doble de código para llegar al mismo lugar. – Jay

1

Razón muy simple: riesgo de colisiones ... Un código hash tendrá valores mucho menos posibles que una cadena. Depende del tipo de hash que genere, pero tomemos un ejemplo muy simple, donde se agregarían los valores ordinales de las letras, multiplicados por su posición: a = 1, b = 2, etc. Por lo tanto, 'hola' haría traducir a: h: 8x1 = 8, e: 5x2 = 10, l: 12x3 = 36, l: 12x4 = 48, o: 15x5 = 75. 8 + 10 + 36 + 48 + 75 = 177.

¿Hay otros valores de cadena que podrían terminar en 177 hash? ¡Por supuesto! Un montón de opciones Siéntase libre de calcular algunos.

Aún así, este método hash utiliza un método simple. Java y .NET utilizan un algoritmo de hash más complejo con muchas menos posibilidades de tales colisiones. Pero aún así, existe la posibilidad de que dos cadenas diferentes den como resultado el mismo valor hash, por lo que este método es menos confiable.

+0

zzzda también sería igual a 177. (1x26, 2x26, 3x26, 4x4, 5x1) –

36

Déjame darte un ejemplo de contador. Prueba de esto,

public static void main(String[] args) { 
    String str1 = "0-42L"; 
    String str2 = "0-43-"; 

    System.out.println("String equality: " + str1.equals(str2)); 
    System.out.println("HashCode eqauality: " + (str1.hashCode() == str2.hashCode())); 
} 

El resultado en mi Java,

String equality: false 
HashCode eqauality: true 
+0

¿Es una coincidencia que estas dos cadenas con la colisión del código hash sean tan similares? (es decir, los primeros tres caracteres son iguales.) ¿O hay dos cadenas similares que también tienen una colisión de código hash? – ep4169

4

Usted puede conseguir el efecto deseado con String.intern() (que se implementa utilizando una tabla hash.)

Se puede comparar el devuelva los valores de intern() usando el operador ==. Si se refieren a la misma cadena, las cadenas originales eran equivalentes (es decir, equals() habrían devuelto true), y solo requiere una comparación de puntero (que tiene el mismo costo que una comparación int).)

String a = "Hello"; 
String b = "Hel" + "lo"; 

System.out.println(a.equals(b)); 
System.out.println(a == b); 

String a2 = a.intern(); 
String b2 = b.intern(); 

System.out.println(a2.equals(b2)); 
System.out.println(a2 == b2); 

Salida:

true 
false 
true 
true 
15

como muchos dijeron hashCode no lo hace singularidad de garantía. de hecho, no puede hacer eso por una razón muy simple.

hashCode devuelve un int, lo que significa que hay 2^32 valores posibles (alrededor de 4.000,000,000), pero seguramente hay más de 2^32 cadenas posibles, lo que significa que al menos dos cadenas tienen el mismo valor de código hash.

esto se llama Pigeonhole principle.

+0

buena respuesta, podría haberlo hecho al conocer el principio del casillero en una entrevista reciente – Karl

+0

Manera más de dos, si lo piensas bien. Si la longitud máxima de una cadena es Integer.MAX_VALUE, es decir, alrededor de 2 mil millones, y hay 2^32 ~ = 64k posibles valores de char, entonces hay (2^32)^(2^31) cadenas posibles frente a 2^32 valores hash, significa que hay (2^32)^(2^31)/(2^32) = (2^32)^(2^30) cadenas para cualquier código hash dado. Que sale a alrededor de 10^9 mil millones. es decir, un número muy grande. – Jay

+0

@Jay: "2^32 ~ = 64k"? ¿Qué significa ~ = aquí? – bacar

7

Otros han señalado por qué no funcionará. Así que solo agregaré el apéndice de que la ganancia sería mínima de todos modos.

Cuando compara dos cadenas en Java, la función String equals comprueba primero si son dos referencias al mismo objeto. Si es así, inmediatamente devuelve verdadero. Luego verifica si las longitudes son iguales. Si no, devuelve falso. Solo entonces comienza a comparar personaje por personaje.

Si está manipulando datos en la memoria, la comparación del mismo objeto puede manejar rápidamente el "mismo" caso, y eso es una comparación rápida, umm, de 4 bytes, creo. (Alguien me corrige si tengo la longitud de un objeto manejar mal.)

Para la mayoría de las cuerdas desiguales, apostaría a que la longitud de comparar rápidamente los encuentra no iguales. Si compara dos nombres de cosas (clientes, ciudades, productos, lo que sea), generalmente tendrán una duración desigual. Por lo tanto, una comparación simple simple los descarta rápidamente.

El peor caso para el rendimiento va a ser dos largas, idénticas, pero no las mismas cadenas de objetos. Luego tiene que hacer el objeto manejar comparar, falso, seguir comprobando. La duración se compara, es verdad, sigue comprobando. Luego, carácter por personaje a través de toda la longitud de la cadena para verificar que sí, de hecho, son iguales hasta el final.

+0

excelente respuesta – Karl

+0

Buena respuesta. Esto se habría beneficiado de la mención del método interno que hace que el estricto control de igualdad tenga mayor importancia. – Spina

+0

@Spina Punto válido. Si ambas cadenas son literales de cadena, entonces sí, sus objetos se compararán igual. Si se leen de una base de datos o de una entrada del usuario, puede internarlos para que sus objetos se igualen. Si esto tiene algún valor depende de la frecuencia con que se comparen las cadenas. – Jay

0

Dos cadenas diferentes pueden generar fácilmente el mismo código hash o diferentes códigos hash. Si quiere una prueba de igualdad, el código hash no dará un resultado único. Cuando usamos la clase String devolverá un valor diferente de código hash. Así que la clase String buffer debería aplicarse para tener el mismo código hash para cada objeto concatenado.

Cuestiones relacionadas