2012-03-10 15 views
16

Mam problem z zapisem metody hashCode() dla utworzonej przeze mnie klasy. Ta klasa jest przeznaczona do użycia w TreeSet i jako taka implementuje Porównywalne. Klasa ma następujące zmienne:Tworzenie metody hashCode() - Java

public class Node implements Comparable<Node> { 
    Matrix matrix; 
    int[] coordinates= new int[2]; 
    Node father; 
    int depth; 
    int cost; 

Tutaj jest wdrożenie metody compareTo(). Chcę, aby TreeSet organizował struktury węzłów według ich kosztu, dlatego też compareTo() zwraca wynik prostego odejmowania.

public int compareTo(Node nodeToCompare) { 
    return this.cost - nodeToCompare.cost; 
} 

Zaimplementowałem również metodę equals().

public boolean equals(Object objectToCompare) { 
    if(objectToCompare== this) {return true;} 
    if(objectToCompare== null || objectToCompare.getClass()!= this.getClass()) {return false;} 

    Node objectNode= (Node) objectToCompare; 
    return this.father.equals(objectNode.father) && 
      this.depth== objectNode.depth && 
      this.cost== objectNode.cost && 
      this.matrix.equals(objectNode.matrix) && 
      Arrays.equals(this.coordinates, objectNode.coordinates); 
} 

Powiedziawszy to wszystko, mam kilka pytań:

  1. Odkąd wprowadził nową metodę equals(), należy wdrożyć nową metodę hashCode()?
  2. Jak mogę wdrożyć nowy kod hashCode method() z tymi zmiennymi? (Zwróć uwagę, że macierz zmiennej typu Matrix ma zaimplementowaną metodę hashCode())

To wszystko!

Odpowiedz

19

Twoja metoda compareTo nie jest zgodna z metodą equals: metoda compareTo mówi, że dwa przypadki są równoważne, jeśli mają takie same cost — tak, że TreeSet mogą tylko kiedykolwiek zawierać co najwyżej jedną instancję danej cost — ale twoja equals Metoda mówi, że są one tylko równoważne, jeśli mają takie same costi są takie same na różne inne sposoby.

Zakładając więc, że metoda equals jest poprawna:

  • trzeba naprawić metodę compareTo być z nim zgodne.
  • należy utworzyć zgodną z nim metodę hashCode. Polecam używanie tej samej logiki, która jest używana przez java.util.List.hashCode(), która jest prostym i skutecznym sposobem na zestawianie kodów mieszających obiektów składowych w określonej kolejności; w zasadzie napisałbyś coś takiego:
    int hashCode = 1; 
    hashCode = 31 * hashCode + (father == null ? 0 : father.hashCode()); 
    hashCode = 31 * hashCode + depth; 
    hashCode = 31 * hashCode + cost; 
    hashCode = 31 * hashCode + matrix.hashCode(); 
    hashCode = 31 * hashCode + java.util.Arrays.hashCode(coordinates); 
    return hashCode;
+0

Ale jeśli zmienię metodę compareTo, czy to nie zmieni sposobu organizacji przedmiotów w TreeSet? –

+1

@ GonçaloLourenço: Tak. To mój punkt; teraz, jeśli 'n1.cost == n2.cost', to' TreeSet', który zawiera 'n1', nie może zawierać' n2'. Czy to naprawdę chcesz? – ruakh

+0

Zdecydowanie nie. Chyba będę musiał użyć czegoś innego zamiast TreeSet. Ponieważ byłeś pierwszym, który odpowiedział na moje pytanie, zaznaczę twoją odpowiedź jako właściwą. Dzięki za pomoc! –

7

Intellij IDEA może to zrobić jako "kliknięcie prawym przyciskiem". Samo zrobienie tego poprawnie uczy cię dużo.

I w każdym przypadku należy zastąpić oba.

+2

Podobny w Eclipse: Źródło> Generuj hashCode() i equals() ... –

+0

@ToKra w moim przypadku , dodała tę linię 'result = prime * result + getOuterType(). hashCode();', ale chciałem tylko, aby pola miały spójny hashcode (między ponownymi załadowaniami aplikacji), więc komentując tę ​​linię działało poprawnie. To była wewnętrzna klasa, dlatego pojawiła się ta linia. –

3

Umowa dla metody hashCode stwierdza, że ​​jeśli dwa obiekty są równe, wywołanie hashCode() powinno dać ten sam wynik liczby całkowitej. Odwrotna nie musi być prawdą, tj. Jeśli dwa hashKody są takie same, obiekty nie muszą się równać.

Patrząc na metodę równości (która wymaga tłumaczenia zmiennej btw), można dodać wartości mieszania wszystkich wewnętrznych zmiennych składowych, które muszą być równe dla danej metody równości, aby dać wartość true. na przykład

public int hashCode() { 
    return this.matrix.hashCode() + 
      this.coordinates[0] + 
      this.coordinates[1] + 
      this.father.hashCode() + 
      this.depth + this.cost;    
} 

Powyższe zakłada, że ​​matryca i ojciec nigdy nie są wartości null, trzeba upewnić się, że sprawdzić null jeśli nie jest to przypadek.

Jeśli czujesz się bardziej ryzykowny, możesz pomnożyć kilka z powyższych, aby uzyskać pewność, że nie uzyskasz kolizji hashCode dla różnych danych (to pomoże zwiększyć wydajność, jeśli używasz swojej klasy w hashtables i hashMaps). Jeśli potrzebujesz, aby zaspokoić null, powyższa metoda może być napisany nieco lepiej tak:

public int hashCode() { 
    return ((this.matrix == null) ? 0 : this.matrix.hashCode()) + 
      17 * this.coordinates[0] + 
      this.coordinates[1] + 
      ((this.father == null) ? 0 : this.father.hashCode()) + 
      31 * this.depth + 19 * this.cost;    
} 
+0

Zauważ, że 'father' jest typu' Node', więc jeśli nigdy nie jest 'null', twoje podejście spowoduje nieskończoną rekursję i przepełnienie stosu. – ruakh

+0

Tak, bałem się tego. Chyba będę musiał zmienić kilka rzeczy. Dzięki za odpowiedź na moje pytanie! –

+0

To prawda, dlatego musimy założyć, że a) albo ojciec jest nullable, albo b) nie ma żadnych okrągłych ścieżek na wykresie. Jeśli żadne z powyższych nie jest prawdziwe, wówczas powinniśmy wykluczyć ojca z obliczeń hashCode. – ksymeon

0

Jeśli kolekcja jest niewielka można powrócić stała od metody hashcode. Używa się go do szybkiego znalezienia. hashCodes przypomina pola, które przechowują elementy. Reguły są następujące:

  1. Równe elementy muszą znajdować się w tym samym polu (mają taki sam kod skrótu) - z pewnością;
  2. Nie równe elementy mogą być w tym samym lub w różnych polach.

Następnie powrót stała, będziecie słuchać tych 2 zasad, ale może znacznie zmniejszyć osiągów na nie małych listach (ponieważ JVM będzie szukać we wszystkich elementach, a nie elementów w tym samym polu tylko). Ale stała powrotna to złe podejście.

PS: Przepraszam za moje pisanie. Angielski nie jest moim ojczystym językiem.

PPS: przeważnie trzeba zaimplementować metodę hashCode w taki sam sposób, jak równy z równym (stosować te same elementy)