Implementarea Java Array HashCode (Programare, Java, Număr Întreg, Hashcode)

souLTower a intrebat.

Acest lucru este ciudat. Un coleg de muncă a întrebat despre implementarea lui myArray.hashCode() în java. Am crezut că știu, dar apoi am făcut câteva teste. Verificați codul de mai jos. Lucrul ciudat pe care l-am observat este că atunci când am scris primul sys out rezultatele au fost diferite. Observați că este aproape ca și cum ar raporta o adresă de memorie și modificarea clasei a mutat adresa sau ceva de genul acesta. M-am gândit să împărtășesc.

int[] foo = new int[100000];
java.util.Random rand = new java.util.Random();

for(int a = 0; a < foo.length; a++) foo[a] = rand.nextInt();

int[] bar = new int[100000];
int[] baz = new int[100000];
int[] bax = new int[100000];
for(int a = 0; a < foo.length; a++) bar[a] = baz[a] = bax[a] = foo[a];

System.out.println(foo.hashCode() + " ----- " + bar.hashCode() + " ----- " + baz.hashCode() +  " ----- " + bax.hashCode());

// returns 4097744 ----- 328041 ----- 2083945 ----- 2438296
// Consistently unless you modify the class.  Very weird
// Before adding the comments below it returned this:
// 4177328 ----- 4097744 ----- 328041 ----- 2083945


System.out.println("Equal ?? " +
  (java.util.Arrays.equals(foo, bar) && java.util.Arrays.equals(bar, baz) &&
  java.util.Arrays.equals(baz, bax) && java.util.Arrays.equals(foo, bax)));

4 răspunsuri
MahdeTo

The java.lang.Array hashCode este moștenită de la Object, ceea ce înseamnă că hashcode-ul depinde de referință. Pentru a obține codul hash pe baza conținutului array-ului utilizați Arrays.hashCode.

Atenție însă, este o implementare superficială a codului hash. Există, de asemenea, o implementare profundă Arrays.deepHashCode.

Comentarii

  • Mulțumesc pentru acest răspuns, dar de ce java.lang.Array nu suprascrie metodele hashCode (și toString) în mod implicit? Există vreun motiv întemeiat? –  > Por Krzysztof Kaczor.
  • Pentru că hashCode trebuie să fie rapid pentru a fi util (deoarece este folosit în principal pentru a preveni un apel costisitor al .equals) și chiar și un hashCode cu valoare superficială pe un array ar putea fi foarte lent. Un hashCode care este practic aleatoriu nu dăunează, dar nu oferă niciun avantaj. Este cel mai mic dintre două rele. –  > Por Torque.
  • @Torque Nu dăunează decât dacă equals() este un rahat în același mod. În mod normal, un hashCode care este „practic aleatoriu” ar fi o problemă serioasă, deoarece dacă equals este adevărat, atunci hashCode trebuie să fie același. O constantă ar fi mai bună decât un cod aleatoriu. –  > Por Mark.
  • Am scris comentariul cu mult timp în urmă, așa că nu pot spune la ce mă gândeam la momentul respectiv, dar probabil că mă refeream la codul hash al sistemului, care va returna un număr care nu se bazează pe câmpurile obiectului (deci „practic aleatoriu”), dar care, desigur, rămâne același pentru obiect între apeluri. Evident, am formulat greșit comentariul. –  > Por Torque.
erickson

Array-urile utilizează codul hash implicit, care se bazează pe locația de memorie (dar nu este neapărat de locația de memorie, deoarece este doar un int și toate adresele de memorie nu se vor potrivi). Puteți vedea acest lucru prin imprimarea, de asemenea, a rezultatului lui System.identityHashCode(foo).

Array-urile sunt doar equal dacă sunt aceeași matrice, identică. Așadar, codurile hash ale tablourilor vor fi egale, în general, numai dacă sunt același tablou identic.

Comentarii

  • (iar obiectele sunt mutate în memorie și, dacă vă uitați la codurile hash, acestea nu arată de obicei ca adrese) –  > Por Tom Hawtin – tackline.
  • Și pentru recent versiuni recente ale Java, comportamentul implicit al JVM este de a nu mai bază codul hash de identitate pe o adresă de memorie. –  > Por Stephen C.
James

Implementarea implicită pentru Object.hashCode() este, într-adevăr, să returneze valoarea pointerului obiectului, deși acest lucru depinde de implementare. De exemplu, o JVM pe 64 de biți poate lua pointerul și să facă XOR și cuvintele de ordinul mare și mic împreună. Subclasele sunt încurajate să suprascrieți acest comportament, dacă acest lucru are sens.

Cu toate acestea, nu are sens să se efectueze comparații de egalitate pe array-uri mutabile. Dacă un element se modifică, atunci cele două nu mai sunt egale. Pentru a menține invarianta conform căreia același tablou va returna întotdeauna același hashCode, indiferent de ceea ce se întâmplă cu elementele sale, tablourile nu suprascriu comportamentul implicit al hashcode-ului.

Rețineți că java.util.Arrays oferă o implementare deepHashCode() pentru cazurile în care este importantă o hashing bazată pe conținutul tabloului, mai degrabă decât pe identitatea tabloului în sine.

Comentarii

  • VM-urile moderne deplasează obiectele în memorie. O adresă curentă poate fi utilizată ca sămânță, dar rezultatul trebuie să fie stocat. –  > Por Tom Hawtin – tackline.
  • Deplasarea în memorie nu determină încă modificarea hashCode. –  > Por Arun R.
Carl Pritchett

Sunt de acord cu utilizarea java.util.Arrays.hashCode (sau a învelișului generic Google Guava Objects.hashcode), dar fiți atenți că acest lucru poate cauza probleme dacă utilizați Terracotta – a se vedea acest link