2009-01-14 8 views
10

La documentación para -hash dice que no debe cambiar mientras se almacena un objeto mutable en una colección, y de manera similar la documentación para -isEqual: dice que el valor -hash debe ser el mismo para objetos iguales.Técnicas para implementar -hash en objetos de cacao mutables

Teniendo en cuenta esto, ¿alguien tiene alguna sugerencia para la mejor forma de implementar -hash de modo que cumpla estas dos condiciones y se calcule de forma inteligente (es decir, no devuelva 0)? ¿Alguien sabe cómo hacen las versiones mutables de las clases proporcionadas por el framework?

Lo más simple es olvidar la primera condición (no cambiar) y simplemente asegurarme de que nunca accidentalmente mute un objeto mientras está en una colección, pero me pregunto si hay alguna solución que sea más flexible.

EDIT: Me pregunto aquí si es posible mantener los 2 contratos (donde los objetos iguales tienen los mismos valores hash, y los hashes no cambian mientras el objeto está en una colección) cuando estoy mutando el interior estado del objeto. Mi inclinación es decir "no", a menos que haga algo estúpido como siempre devolver 0 para el hash, pero es por eso que estoy haciendo esta pregunta.

+0

adivina que esta es una vieja pregunta, acabo de encontrarla ... pero no son objetos mutables usados ​​como claves en una colección generalmente copiada? ¿Eso no solo deja de lado el problema? – nielsbot

+0

@nielsbot: solo se copian las claves para NSDictionaries. NSSet no copia sus objetos, y la API 'CFDictionarySetValue()' tampoco copia sus claves. –

+0

'CFDictionarySetValue' si pasa' kCFTypeDictionaryKeyCallbacks' a 'CFDictionaryCreate', ¿no? Los documentos son casi no sensuales ... Supongo que un obj de colección mutable podría, uh, almacenar en caché los valores de hash, que es lo mismo que asumir que un obj mutable en una colección no cambiará su hash, ¿verdad? – nielsbot

Respuesta

2

Interesante pregunta, pero creo que lo que quiere es lógicamente imposible. Digamos que comienzas con 2 objetos, A y B. Ambos son diferentes y comienzan con diferentes códigos hash. Usted agrega ambos a alguna tabla hash. Ahora, quiere mutar A, pero no puede cambiar el código hash porque ya está en la tabla. Sin embargo, es posible cambiar A de tal manera que sea .equals() B.

En este caso, usted tiene 2 opciones, ninguna de las cuales funciona:

  1. Cambiar el código hash de la A a la igualdad B.hashcode, lo que viola la restricción de no cambiar códigos hash mientras que en una tabla hash.
  2. No cambie el código hash, en cuyo caso A.equals (B) pero no tienen los mismos códigos hash.

Me parece que no hay forma posible de hacerlo sin usar una constante como código hash.

+0

Eso es lo que pensé, y creo que probablemente sea correcto. –

-2

En Java, la mayoría de las clases mutables simplemente no anulan Object.hashCode() de modo que la implementación predeterminada devuelve un valor que se basa en la dirección del objeto y no cambia. Tal vez sólo sea la misma con Objective C

+2

Excepto que viola la regla de que los objetos iguales necesitan tener el mismo hash. La implementación predeterminada para -isEqual: simplemente compara punteros, pero lo anulo para hacer una comparación basada en valores de los campos en el objeto. –

2

Mi lectura de la documentación es que el valor de un objeto mutable para hashpuede (y probablemente debería) el cambio cuando está mutado, pero no debe cambio cuando el objeto no ha sido mutado La parte de la documentación a la que hacer referencia, por lo tanto, es: "No mute los objetos que están almacenados en una colección, porque eso hará que cambie su valor hash".

Para citar directamente de la NSObject documentation for hash:

Si se añade un objeto mutable a una colección que utiliza valores de hash a determinar la posición del objeto en la colección , el valor devuelto por el método de hash del objeto no debe cambiar mientras el objeto está en la colección . Por lo tanto, ya sea el método de hash no debe depender de ninguna información de estado interno del objeto o debe asegurarse información de estado interno del objeto no cambio mientras el objeto está en la colección .

(El subrayado es mío.)

+0

Sí, esa es la forma en que lo leo, pero si el hash no se basa en el estado interno que hace que sea bastante difícil hacer el mismo hash de objetos iguales. Esta es la razón por la que estoy haciendo esta pregunta, para averiguar si alguien tiene una solución inteligente para almacenar objetos mutables en tablas de mapas. –

+0

Si está buscando trucos inteligentes para esto, no creo que pueda ayudar, pero tenga en cuenta que la documentación dice que el método hash no debe basarse en el estado interno * o * el estado interno debe garantizarse que no cambiará mientras en la colección. Puede usar el estado interno si cumple con este último. –

+0

Al volver a leer la pregunta: ¿está preguntando si hay una forma de mantener el hash e isEqual: contrato al mutar un objeto en una colección, o simplemente si es posible mantener el contrato en general? Estaba respondiendo a la segunda pregunta, que puede no ser lo que estaba preguntando. –

0

Dado que ya está anulando -isEqual: para hacer una comparación basada en el valor, ¿está seguro de que realmente necesita molestarse con -hash?

No puedo adivinar exactamente para qué necesita esto, por supuesto, pero si quiere hacer una comparación basada en valores sin desviarse de la implementación esperada de -isEqual: solo devolver SÍ cuando los hash son idénticos, un mejor enfoque podría ser para imitar NSString's -isEqualToString :, para crear su propio método -isEqualToFoo: en lugar de usar o anular -isEqual :.

+1

Los documentos del protocolo NSObject para esto indican explícitamente que anular -isEqual: y no -hash es incorrecto, y puede causar problemas cuando una instancia de la clase se almacena en una colección. Además, NSString anula -isEqual: para llamar -isEqualToString: si ambos objetos son cadenas. Consulte este documento de Apple: http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple%5Fref/doc/uid/TP40002974-CH4-SW25 –

1

La pregunta aquí no es cómo cumplir con estos dos requisitos, sino con cuál de ustedes debe cumplir. En la documentación de Apple, se establece claramente que:

un diccionario mutable puede colocarse en una tabla hash pero no debe cambiarla mientras está allí.

Dicho esto, parece más importante que cumpla con los requisitos de igualdad de hashes. El hash de un objeto siempre debe ser una forma de verificar si un objeto es igual a otro. Si este no es el caso, no es una verdadera función hash.

Solo para finalizar mi respuesta, daré un ejemplo de una buena implementación de hash. Digamos que está escribiendo la implementación de -hash en una colección que ha creado. Esta colección almacena una matriz de NSObjects como punteros. Dado que todos los NSObjects aplicar la función hash, puede utilizar sus hash para calcular el hash de la colección:

- (NSUInteger)hash { 
    NSUInteger theHash = 0; 
    for (NSObject * aPtr in self) { // fast enumeration 
     theHash ^= [aPtr hash]; 
    } 
    return theHash; 
} 

De esta manera, dos objetos de colección que contienen los mismos punteros (en el mismo orden) tendrán el mismo hash.

Cuestiones relacionadas