2012-02-03 16 views
5

Mam proste niestandardowe klasy Point w następujący sposób i chciałbym wiedzieć, czy moja implementacja hashCode mogłaby zostać ulepszona, czy jest to najlepsze, co dostanie.Java hashCode dla klasy Point

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

Jak starasz poprawić? Czy chcesz spróbować zrobić to szybciej? – David

+0

chcesz zagwarantować wyjątkowość? prędkość? – Adrian

+0

Chciałbym zagwarantować oba :) –

Odpowiedz

8

Proszę nie używać ciągów. Istnieje wiele teorii związanych z tą i kilkoma implementacjami (metoda dzielenia, mnożenia itd.). Jeśli masz około godzinę można oglądać ten MIT-Class

To zostało powiedziane, jest tu co Netbeans 7.1 proponuje:

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

października 2015 Edycja

zacząłem używać IntelliJ jakiś czas temu, Teraz jestem szczęśliwszy. To właśnie wytwarza jego automatyczne generowanie hashCode. To trochę mniej gadatliwe. Zwróć też uwagę na użycie liczb pierwszych.

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

+1 Interesujące, że Netbeans sugeruje coś innego niż zaćmienie, ale podstawa wdrożenia jest podobna i solidna. – nybbler

+0

Jestem zdezorientowany, w jaki sposób druga implementacja jest dobrą implementacją? Dostajesz kolizje nawet z bardzo małymi liczbami, takimi jak (0,31), (1,0). To wydaje się bardzo szkodliwe, nie? –

+0

@ ChristopherShroba yours jest bardzo interesującym komentarzem i zajrzę do tego, gdy wrócę z wakacji! Głównym problemem jest to, że 'wynik' jest inicjowany z' 0' przy wprowadzeniu próbki. Jednak tak właśnie działa IntelliJ 2016 ... – Gevorg

0

kiedyś napisać własny hash i wynosi funkcje potem znalazłem to:)

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); 
} 

oczywiście pamiętać, co następuje:

Ponieważ odbicie obejmuje typy, które są dynamicznie rozwiązywane, niektóre optymalizacje maszyny wirtualnej Java nie mogą być wykonywane. W związku z tym operacje refleksyjne charakteryzują się niższą wydajnością niż ich odpowiedniki przeciwodblaskowe w wersji i należy ich unikać w sekcjach kodu , które są często wywoływane w aplikacjach wrażliwych na wydajność. SRC

+0

Ponieważ istnieją tylko dwa pola, można również użyć tej biblioteki, ale jawnie wylistować pola. –

+0

Jeśli ta klasa jest istotnie używana w kolekcji, odblaskowy kod haszujący będzie hitem dużej wydajności. – user949300

+1

Zalecam użycie ['HashCodeBuilder.append'] (http://commons.apache.org/lang/api-2.5/org/apache/commons/lang/builder/HashCodeBuilder.html#append%28int%29) zamiast tego. –

2

Polecam stosując prostszą i bardziej wydajnych metod bez strun, może metoda Josha Blocha z this answer, w twoim przypadku tylko:

return 37 * x + y; 

EDIT: nybbler jest poprawna. Co jest rzeczywiście zalecane jest:

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

To nie jest dokładnie to, co jest zalecane w połączonej odpowiedzi. Tęskniłeś, że powinno to być zrobione dla każdego pola ** indywidualnie **, nie w tym samym czasie. Ten algorytm generuje taki sam wynik dla [0,37] i [1,0] – nybbler

+0

. Zauważ, że nowa implementacja nadal będzie powodować kolizję dla dowolnej pary punktów w formie (x, 37y), (y, 37x). . – Gevorg

0

Od klasy Point w JDK (odziedziczone Point2d):

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

To wygląda nieco lepiej niż implementacji.

0

Możesz zajrzeć do istniejącego typu Punkt klas implementacji:

/** 
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  } 

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

Prosty przewodnik dla realizacji hashcode można znaleźć here

+0

Słowo ostrzeżenia dla wszystkich osób używających tego do identyfikacji. Zderzenia mieszania są rzeczywistością ... E.g. pastebin.com/6wM3W3Wv – vincent

0

Domyślnie Eclipse użyje Funkcja hashCode() dla klasy Point podobna do:

@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; 
} 

Co najmniej włączenie liczby pierwszej do algorytmu hashCode pomoże w "unikalności".

+0

Być może napisałeś błędnie "Java" zamiast "Eclipse". Domyślnie 'hashCode' jest zwykle implementowany poprzez konwersję wewnętrznego adresu obiektu na liczbę całkowitą." –

+0

@MatthewFlaschen Rzeczywiście zrobiłem. Zaktualizowano teraz; dzięki za złapanie tego. – nybbler

5

Instrukcja mnożenie wartości wszystkich istotnych dziedzinach członkowskich zgodnie z sugestią Gevorg jest prawdopodobnie najbardziej skuteczny i ma dobry rozkład wartości. Jednakże, jeśli sprzyjają czytelności, są tam ładne alternatywy dostępne albo w Java 7 ...

import java.util.Objects; 

... 

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

... lub w Guava Biblioteka:

import com.google.common.base.Objects; 

.... 

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

Obie te metody varags prostu przekazać Arrays.hashCode(Object[] a), więc jest niewielki wpływ na wydajność ze względu na autoboxing ints i tworzenie tablicy odwołań do obiektów, ale powinno być znacznie mniej znaczące niż użycie refleksji.

I czytelność jest po prostu świetny, ponieważ po prostu zobaczyć, które pola są wykorzystywane do obliczeń hashcode i wszystkich mnożenie i dodawanie składnia jest tylko ukryta pod maską 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

Nadal podatny na dowolne pary w postaci '(x, 31y), (y, 31x)' np. (0, 31), (1, 0) lub (3, 217), (7, 93). Próbuję tu zainicjować dyskusyjną dyskusję. Czy istnieje sposób na bardziej niezawodną implementację lub z zaledwie 2 liczbami całkowitymi, z którymi trzeba poradzić sobie z tego rodzaju problemem (który zależy od liczby pierwszej używanej w generowaniu kodu skrótu)? – Gevorg

Powiązane problemy