2011-12-16 12 views
7

TreeSet tiene un constructor que toma un comparador, lo que significa que incluso si los objetos que almacena no son objetos Comparable por sí mismos, puede proporcionar un comparador personalizado.TreeSet/TreeMap equivalente para HashSet/HashMap (hasher personalizado)

¿Existe una implementación análoga de un conjunto sin orden? (Por ejemplo, una alternativa a HashSet<T> que toma un objeto "hasher" que calcula equals() y hashCode() para objetos T que pueden ser diferentes de las propias implementaciones de los objetos?)

C++ std::hash_set que esto da, sólo me preguntaba si hay algo para Java .


Editar: @Max trae un buen punto técnica sobre equals() - justo lo suficiente; y es cierto para las claves TreeMap y HashMap a través de Map.containsKey(). Pero, ¿hay otras estructuras de datos conocidas que permitan la organización mediante hashers personalizados?

+0

Por cierto, ¿estás seguro de que no están mezclando diferentes dominios de objetos? En general, no tiene problemas para agregar nuevos métodos en los objetos que están en el dominio de su aplicación. Sin embargo, si intenta hacer un mapa de los objetos recibidos de un cliente Axis generado (por ejemplo), entonces está mezclando diferentes dominios: el dominio del servicio web y el dominio de su aplicación. Lo que significa que, en esencia, nunca deberías necesitar lo que estás pidiendo. – bezmax

Respuesta

9

No, tener un objeto "hasher" no es compatible con las especificaciones Collections. Sin duda, puede implementar su propia colección que admita esto, pero otra forma de hacerlo es considerar el Hasher como un objeto de envoltura que almacene en su HashSet.

Set<HasherWrapper<Foo>> set = new HashSet<HasherWrapper<Foo>>(); 
set.add(new HasherWrapper(foo)); 
... 

La clase contenedora sería entonces algo así:

private class HasherWrapper<T> { 
    T wrappedObject; 
    public HasherWrapper(T wrappedObject) { 
     this.wrappedObject = wrappedObject; 
    } 
    @Override 
    public int hashCode() { 
     // special hash code calculations go here 
    } 
    @Override 
    public boolean equals(Object obj) { 
     // special equals code calculations go here 
    } 
} 
+1

Buena solución a la pregunta. Una sugerencia, debes hacer que 'wrappedObject' sea el final. Además, 'wrappedObject' debe ser público o privado con un getter. –

+0

¿Por qué final? ¿Eso realmente ayuda mucho con el punto de acceso? Dado que es una clase privada, permitiría que la clase externa simplemente acceda al campo sin un getter. – Gray

+0

Varias referencias web dicen que la final no es necesaria en estos días porque las máquinas virtuales son inteligentes y saben si algo se ha anulado. http://stackoverflow.com/questions/4279420/does-use-of-final-keyword-in-java-improve-the-performance – Gray

0

Definitivamente no hay nada como eso, hashcode() y equals() son atributos de definición de un objeto y no deberían modificarse. Definen qué hace que un objeto sea igual entre sí y esto no debería ser diferente de un conjunto a otro. La única forma de hacer lo que está diciendo es subclasificar el objeto y escribir un nuevo hashcode() y equals() y esto realmente solo tendría sentido si la subclase tuviera una variable de definición que debería agregarse además de la superclase 'hashcode() y equals(). Sé que puede que esto no sea lo que pretendes, pero espero que esto ayude. Si explica su razonamiento más por querer esto, entonces podría ayudar encontrar una mejor solución si existe.

+5

En realidad, la forma en que decida hacer hashing y comparar objetos en su programa no define atributos de los objetos, sino del programa. No hay nada de malo en querer utilizar un algoritmo hash diferente que sepa que es mejor para su conjunto de problemas particular, o considerar objetos iguales si tienen el mismo nombre, o la misma identificación, o alguna otra cosa. Depende del programa, y ​​este es un área donde el diseño de Java deja mucho que desear. –

1

No, no existe y no puede haberlo por especificación. Además, entendiste mal la forma en que TreeSet usa es Comparator.

De TreeSet Javadoc:

Tenga en cuenta que el orden mantenido por un conjunto (si no se proporciona una explícita comparador) debe ser consistente con los iguales si se va a correctamente implementar la interfaz Set. (Consulte Comparable o Comparador para obtener una definición precisa de igual a igual). Esto es tan porque la interfaz Set se define en términos de la operación igual, pero una instancia TreeSet realiza todas las comparaciones de elementos usando su compareTo (o compare) método, por lo que dos elementos que se consideran iguales por este método son, desde el punto de vista del conjunto, iguales. El comportamiento de un conjunto está bien definido, incluso si su orden es incoherente con igual; simplemente no obedece el contrato general de la interfaz Set .

De Comparable javadoc:

El ordenamiento natural para una clase C se dice que es consistente con es igual a si y sólo si e1.compareTo (e2) == 0 tiene el mismo valor booleano como e1.equals (e2) para cada e1 y e2 de la clase C. Tenga en cuenta que null no es instancia de cualquier clase, y e.compareTo (null) debería arrojar NullPointerException aunque e.equals (null) devuelve falso.

De Collection javadoc:

boolean contains (Object o)

devuelve verdadero si esta colección contiene el elemento especificado. Más formalmente, devuelve verdadero si y sólo si esta colección contiene al menos un elemento tal que e (O == null e == null:? O.equals (e)).

Por lo tanto, por la especificación no puede ser cualquier tipo de clase que implementa Collection<E> interfaz y totalmente depender de algún objeto de estilo Comparador externa para insertar objetos. Todas las colecciones deben usar el método equals de una clase Object para verificar si el objeto ya está insertado.

+0

Estoy tratando de entender por qué no puedes proporcionar un comparador personalizado diferente que sea consistente con iguales. ¿No e2.compareTo (e1) logra esto (solo invierte el orden, pero los artículos iguales siguen siendo iguales)? –

+0

Porque entonces la especificación de la interfaz de Colección dependerá de la implementación de los programadores del Comparador nombrado. La interfaz de colección establece explícitamente que ** todas ** las implementaciones considerarán objetos iguales si 'a.es igual a (b) '. Si implementa algún tipo de Colección que ignore o ignore parcialmente esa regla (como el caso especificado por el autor), estará rompiendo la especificación de la interfaz de Colección. Incluso si escribe en la documentación "¡El comparador aprobado ** debe ** ser consecuente con los iguales!" - la especificación seguirá estando rota. – bezmax

4

No existe tal aplicación en la biblioteca estándar, pero no le impide rodar su propia. Esto es algo que a menudo quería tener yo mismo.

Ver http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4771660 por la razón:

Queríamos evitar la complejidad. Nos tomamos en serio esta noción en el momento en que se diseñó el marco de colecciones, pero lo rechazamos. La ración de potencia-peso parecía baja. Sentimos que igual era lo que quería el 95% del tiempo; ==, 4%; y algo más, 1%. Escribir contratos sensibles para operaciones masivas cuando es muy complicado cuando los predicados de igualdad difieren .