¿Cuál es el código hash para una clase personalizada que tiene solo dos propiedades int?

In Java, I have a class that represents a point with int coordinates

public class Point {
    int x = -1;
    int y = -1;

    public Point (int xNew, int yNew) {
        x = xNew; y = yNew;
    }

    public boolean equals (Object o) {
        // no need for (o instanceof Point) by design
        return x == ((Point)o).x && y == ((Point)o).y;
    }
}

I'm using objects of class Point como llaves en un HashMap and as elements in a HashSet.

What would be the best candidate for the hashCode function? I would make it double so that the left part is x and the right part is y, for example: x = 4, y = 12, Entonces el hashCode devoluciones 4.12. But by the implementation, it cannot be double, only int.

This is not an option:

public int hashCode() {
    // no need to check for exception parseInt since x and y are valid by design
    return Integer.parseInt(Integer.toString(x) + Integer.toString(y));
}

because values x y y can be too long, so that together they will not be converted.

preguntado el 31 de julio de 12 a las 15:07

int hashCode says return for hashcode method is int only, so double is ruled out. -

Yes, that's why I got stuck for a while. -

8 Respuestas

You can't change the type of hashCode, nor should you want to.

I'd just go with something like:

public int hashCode() {
    return x * 31 + y;
}

Note that this means that (a, b) is different to (b, a) for most cases (unlike e.g. adding or XOR-ing). This can be useful if you often end up with keys for the "switched" values in real life.

It no es unique - but hash codes don't have to be. They sistema económico justo have to be the same for equal values (for correctness), and (for efficiency) "usually" different for non-equal values, with a reasonable distribution.

In general, I usually follow the same kind of pattern as Josh Bloch suggests in Effective Java:

public int hashCode() {
    int hash = 17;
    hash = hash * 31 + field1Hash;
    hash = hash * 31 + field2Hash;
    hash = hash * 31 + field3Hash;
    hash = hash * 31 + field4Hash;
    ...
    return hash;
}

Dónde field1Hash would be the hash code for reference type fields (or 0 for a null reference), the int itself for int values, some sort of hash from 64 bits to 32 for long etc.

EDIT: I can't remember the details of why 31 and 17 work well together. The fact that they're both prime puede be useful - but from what I remember, the maths behind why hashes like this are generally mejor (though not as good as hashes where the distribution of likely values is known in advance) is either difficult or not well understood. I know that multiplying by 31 is cheap (shift left 5 and subtract the original value)...

Respondido 31 Jul 12, 17:07

Could you edit to remind me (or respond via comment) why 31 is often the magic number chosen for hash codes? - NegroVegetal

@JonSkeet Thanks! It is always helpful to glean the details behind your answers. - NegroVegetal

@Matt: Please don't edit answers like that - the code you edited in would have been perfectly fine for a separate answer. - jon skeet

@Matt: You edited my https://www.youtube.com/watch?v=xB-eutXNUMXJtA&feature=youtu.be to edit in some extra code. It's a perfectly reasonable bit of code to include in an answer, but I don't think it makes sense to make it part of my respuesta. - jon skeet

I just saw someone using hash *= 31 + field1Hash en lugar de hash = hash * 31 + field1Hash . DON'T do that. It changes the order of the operators as the part after the *= is evaluated at first and then multiplicated with the current hash. Just my 5 cents warning for people "optimizing" code too quickly. - Ray

I know that it is ok for non-equal objects to have the same hashcodes. However, the more collisions, the worse the performance will be (for example, in a hash table).

As far as I know, the best mapping from Z² → Z is the "elegant pairing function" (google it). Here is the implementation

// x,y must be non-negative
int elegant(int x, int y) {
    return x < y ? y * y + x : x * x + x + y;
}


// returns a unique number for every x,y pair
int elegantSigned(int x, int y) {
    if (x < 0) {
        if (y < 0)
            return 3 + 4 * elegant(-x - 1, -y - 1);
        return 2 + 4 * elegant(-x - 1, y);
    }
    if (y < 0)
        return 1 + 4 * elegant(x, -y - 1);
    return 4 * elegant(x, y);
}

This will begin to overlap as soon as you get multiplication overflow. If the absolute value of x and y is less than about 46000, then this will have cero hash collisions.

Respondido el 14 de enero de 16 a las 21:01

Sólo tiene que utilizar java.util.Objects.hash(Object... values).

public int hashCode() {
    return Objects.hash(field1,field2);
}

Objects.hash actually calls 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;
}

Respondido 27 Abr '19, 05:04

Tenga en cuenta que Objects.hash(3.0f, -1.0f) === Objects.hash(-3.0f, 1.0f) (a través de stackoverflow.com/questions/36848151/…) - phy25

This question is quite old, but I think the very idea will be actual as long as java exist. Let us analyze approaches above:

  1. Objects.hash(...) is fluent and clear what needs to be done, BUT it uses varargs (implicitly creating an array) and moreover, it implicitly boxes every single primitive, being passed into the method.
  2. x * 31 + y is performance-efficient: there is no boxing, no explicit or implicit array creation operations being used. BUT, it is poco claro what needs to be done. Why 31, not 42? For the ones familiar with how hashing works there's no difficulties to understand such code, but what for the others? The second pitfall is that it is difficult to extend: you easily can forget to add new values into the hashing code if you, for example, wanted to go 3D and added z coordinate, because it forces you to copy-paste almost identical code many times.

I can introduce the third approach, not being mentioned in answers above:

@Override
public final int hashCode()
{
    final int[] numbers = {x, y};
    return Arrays.hashCode(numbers);
}

It uses a temporary array to hold integers being hashed, and calling Arrays.hashCode(), which is available since Java 1.5, there's also versions for other primitive types.

Ventajas: Es SECO, fluent and completely clear what needs to be done. It does not suffer from implicit boxing and does not uses implicit vararg. It is relatively fast and cheap. It can be easily extended by adding extra numbers into array initializer.

Desventajas: It is not as fast as copy-paste method. Please consider it if hash code is being called frequently.

Saludos cordiales.

Respondido el 03 de junio de 20 a las 22:06

It's often worth considering HashCodeBuilder de Apache Commons

This class enables a good hashCode method to be built for any class. It follows the rules laid out in the book Effective Java by Joshua Bloch. Writing a good hashCode method is actually quite difficult. This class aims to simplify the process

and I would definitely recommend looking at the referenced book Java eficaz.

Respondido 08 Jul 15, 07:07

There is a common strategy of generating a hashcode operation. In your case this would be :

public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + x;
    result = prime * result + y;
    return result;

}

Respondido 31 Jul 12, 15:07

So just return 31 * (31 + x) + y; - sophie sperner

Es posible que desee echar un vistazo a Google Guayaba Objects.hashCode(Object...) método.

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

Respondido 31 Jul 12, 15:07

try adding their hashcodes. ?

return new Integer(x).hashCode()+new Integer(y).hashCode();

Respondido 31 Jul 12, 15:07

Primitive types do not have hashCode. - sophie sperner

@MarkusMikkolainen Which simply returns the int it's wrapping. It would be simpler to just use x + y. - Jeffrey

ah. I was not aware of the implementation of Integer.hashCode. Usually hashcodes try to avoid returning sequential hashcodes for sequential values.. - Markus Mikkolainen

Please consider not to use new Integer(int) explicitly, use Integer.valueOf(int) instead. There's one little optimization called integer caching helping you to increase performance sometimes. There's also a method Objects.hashCode(Object), you may easily pass your int, and it will be boxed into Integer automatically! Cheers. - Netherwire

That would make Point(x, y) have the same hash code as Point(y, x) - Dimezis

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.