2012-02-03 21 views
5

Tengo una clase de punto personalizada simple de la siguiente manera y me gustaría saber si mi implementación hashCode podría mejorarse o si esto es lo mejor que va a obtener.Java hashCode para una clase de punto

public class Point 
{ 
    private final int x, y; 

    public Point(int x, int y) 
    { 
     this.x = x; 
     this.y = y; 
    } 

    public int getX() 
    { 
     return x; 
    } 

    public int getY() 
    { 
     return y; 
    } 

    @Override 
    public boolean equals(Object other) 
    { 
     if (this == other) 
      return true; 

     if (!(other instanceof Point)) 
      return false; 

     Point otherPoint = (Point) other; 
     return otherPoint.x == x && otherPoint.y == y; 
    } 


    @Override 
    public int hashCode() 
    { 
     return (Integer.toString(x) + "," + Integer.toString(y)).hashCode(); 
    } 

} 
+1

Cómo estás tratando para mejorarlo? ¿Quieres intentar hacerlo más rápido? – David

+0

desea garantizar la singularidad? ¿velocidad? – Adrian

+0

Me gustaría garantizar ambos :) –

Respuesta

8

No utilice cadenas. Existe una gran cantidad de teoría detrás de esto y varias implementaciones (método de división, multiplicación uno, etc.). Si usted tiene acerca de una hora se puede ver este MIT-Class

Dicho esto, aquí es lo que Netbeans 7.1 sugiere:

@Override 
public int hashCode() { 
    int hash = 7; 
    hash = 71 * hash + this.x; 
    hash = 71 * hash + this.y; 
    return hash; 
} 

de octubre de el año 2015 Editar

que empecé a usar IntelliJ hace un tiempo, Yo vivo más feliz ahora. Esto es lo que produce su generación automática hashCode. Es un poco menos detallado. Tenga en cuenta el uso de números primos también.

@Override 
public int hashCode() { 
    int result = x; 
    result = 31 * result + y; 
    return result; 
} 
+0

+1 Es interesante que Netbeans sugiera algo diferente a eclipse, pero la base para la implementación es similar y sólida. – nybbler

+0

Estoy confundido, ¿cómo es la segunda implementación una buena implementación? Obtiene colisiones incluso con números muy pequeños, como (0,31), (1,0). Eso parece muy perjudicial, ¿no? –

+0

@ChristopherShroba ¡el tuyo es un comentario muy interesante y lo investigaré una vez que haya vuelto de las vacaciones! El problema principal es que 'result' se inicializa con' 0' dada su entrada de muestra. Aún así, eso es lo que hace IntelliJ 2016 ... – Gevorg

0

solía escribir mi propio hash y las funciones de igual luego me encontré esto:)

import org.apache.commons.lang.builder.HashCodeBuilder; 
import org.apache.commons.lang.builder.EqualsBuilder; 

@Override 
public boolean equals(Object obj) { 
    return EqualsBuilder.reflectionEquals(this, obj); 
} 
@Override 
public int hashCode() { 
    return HashCodeBuilder.reflectionHashCode(this); 
} 

, por supuesto, tener en cuenta lo siguiente:

Debido a la reflexión, a otros tipos que son resuelto dinámicamente, ciertas optimizaciones de la máquina virtual Java no se pueden realizar. En consecuencia, las operaciones reflexivas tienen un rendimiento más lento que sus contrapartes no reflectantes , y deben evitarse en las secciones de código que se llaman con frecuencia en aplicaciones sensibles al rendimiento. SRC

+0

Dado que solo hay dos campos, también puede usar esta biblioteca, pero enumere explícitamente los campos. –

+0

Si esta clase se usa significativamente en una colección, un hashCode reflexivo será un gran golpe de rendimiento. – user949300

+1

Recomiendo usar el ['HashCodeBuilder.append'] (http://commons.apache.org/lang/api-2.5/org/apache/commons/lang/builder/HashCodeBuilder.html#append%28int%29) método en su lugar. –

2

yo recomendaría usar un método más simple y más performante sin condiciones, tal vez el método de Josh Bloch de this answer, en su caso, simplemente:

return 37 * x + y; 

EDIT: nybbler es correcta. Lo que realmente está recomendada es:

int result = 373; // Constant can vary, but should be prime 
result = 37 * result + x; 
result = 37 * result + y; 
+1

Eso no es exactamente lo que se recomienda en su respuesta vinculada. Te perdiste que esto debería hacerse para cada campo ** individualmente **, no al mismo tiempo. Este algoritmo genera el mismo resultado para [0,37] y [1,0] – nybbler

+0

Tenga en cuenta que la nueva implementación aún generará una colisión para cualquier par de puntos en la forma (x, 37y), (y, 37x). – Gevorg

0

de la clase Punto de JDK (heredado de POINT2D):

public int hashCode() { 
    long bits = java.lang.Double.doubleToLongBits(getX()); 
    bits ^= java.lang.Double.doubleToLongBits(getY()) * 31; 
    return (((int) bits)^((int) (bits >> 32))); 
} 

Eso se ve un poco mejor que su puesta en práctica.

0

Usted puede tener una mirada en clases de implementaciones de tipos de punto existente:

/** 
343  * Returns the hashcode for this <code>Point2D</code>. 
344  * @return a hash code for this <code>Point2D</code>. 
345  */ 
346  public int hashCode() { 
347  long bits = java.lang.Double.doubleToLongBits(getX()); 
348  bits ^= java.lang.Double.doubleToLongBits(getY()) * 31; 
349  return (((int) bits)^((int) (bits >> 32))); 
350  } 

de: http://kickjava.com/src/java/awt/geom/Point2D.java.htm#ixzz1lMCZCCZw

guía simple para la aplicación hashCode se pueden encontrar here

+0

Palabra de advertencia para todos aquellos que usan esto para su identificación. Las colisiones hash son una realidad ... Por ej. pastebin.com/6wM3W3Wv – vincent

0

Por defecto, Eclipse utilizará una Función hashCode() para su clase Point similar a:

@Override 
public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    result = prime * result + getOuterType().hashCode(); 
    result = prime * result + x; 
    result = prime * result + y; 
    return result; 
} 

Por lo menos, incorporar un número primo en su algoritmo hashCode ayudará con su "singularidad".

+0

Quizás escribiste mal "Java" en lugar de "Eclipse". Por defecto, 'hashCode' se implementa típicamente convirtiendo la dirección interna del objeto en un número entero." –

+0

@MatthewFlaschen De hecho lo hice. Actualizado ahora; gracias por atrapar eso. – nybbler

5

La multiplicación manual de los valores de todos los campos de miembros importantes como sugiere Gevorg es probablemente la más eficiente y tiene una buena distribución de valores. Sin embargo, si usted a favor de la legibilidad, hay buenas alternativas disponibles ya sea en Java 7 ...

import java.util.Objects; 

... 

@Override 
public int hashCode() { 
    return Objects.hash(x, y); 
} 

... o en el Guava biblioteca:

import com.google.common.base.Objects; 

.... 

@Override 
public int hashCode() { 
    return Objects.hashCode(x, y); 
} 

Ambos métodos varags simplemente delegar en Arrays.hashCode(Object[] a), por lo que hay un ligero impacto en el rendimiento debido al autoboxing de entradas y la creación de una matriz de referencias a objetos, pero debería ser mucho menos importante que usar la reflexión.

Y la legibilidad es simplemente genial, ya que sólo tiene que ver, qué campos se utilizan para el cálculo código hash y toda la multiplican y agregar la sintaxis es sólo oculta bajo el capó de Arrays.hashCode(Object[] a):

public static int hashCode(Object a[]) { 
    if (a == null) 
     return 0; 

    int result = 1; 

    for (Object element : a) 
     result = 31 * result + (element == null ? 0 : element.hashCode()); 

    return result; 
} 
+0

Aún vulnerable a cualquier par en forma '(x, 31y), (y, 31x)' p. Ej. (0, 31), (1, 0) o (3, 217), (7, 93). Estoy tratando de provocar una amplia discusión aquí. ¿Hay alguna manera de tener una implementación más sólida o con solo 2 enteros con los que tener que lidiar con este tipo de problema (que depende del número primo utilizado en la generación de código hash)? – Gevorg

Cuestiones relacionadas