Save 37% off PRO during our Black Friday Sale! »

Hashons peu mais hashons bien

Hashons peu mais hashons bien

Pour en savoir plus sur hashCode dans le JDK

3550b689279d2c0bbf0f19aa86860705?s=128

olivier_bourgain

April 12, 2015
Tweet

Transcript

  1. #HPHB @OlivierBourgain & @OlivierCroisier Hashons peu, mais hashons bien DEVOXX

    FRANCE 2015
  2. #HPHB @OlivierBourgain & @OlivierCroisier Olivier BOURGAIN Freelance OBMG @OlivierBourgain Olivier

    CROISIER Freelance Moka Technologies @OlivierCroisier thecodersbreakfast.net PRESENTATION
  3. #HPHB @OlivierBourgain & @OlivierCroisier Plan • Identité • Hashcode •

    Hashcode en action • Performances PRESENTATION
  4. #HPHB @OlivierBourgain & @OlivierCroisier Classes de complexité PRESENTATION O(1) O(n)

    O(log n) O(nx)
  5. #HPHB @OlivierBourgain & @OlivierCroisier 2 identités • Identité physique •

    Adresse mémoire • Opérateur == • Identité logique • Modélisation métier • Méthode equals() EQUALS
  6. #HPHB @OlivierBourgain & @OlivierCroisier Contrat • Réflexif • Symétrique •

    Transitif • Stable • Non-null • Cf. javadoc EQUALS
  7. #HPHB @OlivierBourgain & @OlivierCroisier Guide d'implémentation • Test == •

    Test instanceof • Conversion • Comparaison • java.util.Objects.equals() EQUALS
  8. #HPHB @OlivierBourgain & @OlivierCroisier public class BankAccount { private final

    String bankId; private final long accountId; private BigDecimal amount; public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof BankAccount)) return false; BankAccount other = (BankAccount) o; return Objects.equals(this.bankId, other.bankId) && this.accountId == other.accountId; } } Guide d'implémentation EQUALS
  9. #HPHB @OlivierBourgain & @OlivierCroisier Identité immuable • Champs final •

    Autre identité, autre instance • Construction cohérente EQUALS
  10. #HPHB @OlivierBourgain & @OlivierCroisier Identité composite • Classe dédiée •

    Immuable • Value-type • Atomicité EQUALS
  11. #HPHB @OlivierBourgain & @OlivierCroisier Identité composite public class BankAccount {

    private final BankAccountId id; private BigDecimal amount; public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof BankAccount)) return false; BankAccount other = (BankAccount) o; return this.id.equals(other.id); } } EQUALS
  12. #HPHB @OlivierBourgain & @OlivierCroisier Recherche • Opération fréquente • Insertion

    dans les Sets & Maps • Recherche linéaire en O(n) • Optimisation ? HASHCODE
  13. #HPHB @OlivierBourgain & @OlivierCroisier Recherche • Les structures de données

    à la rescousse ! • Arbres • Tables de hachage • Heaps • ... HASHCODE
  14. #HPHB @OlivierBourgain & @OlivierCroisier Hashcode • Fonction de classification •

    Méthode hashCode() • Recherche par identité au sein du groupe • Performance en O(1) • Sensible à la qualité de la fonction HASHCODE
  15. #HPHB @OlivierBourgain & @OlivierCroisier #27 G D #13 B I

    E #42 H C F A contains( ) ? C hashCode( ) = 42 C .equals( , , ...) ? F C A C HASHCODE
  16. #HPHB @OlivierBourgain & @OlivierCroisier Contrat • Stable • Cohérente avec

    l'identité • Cf. javadoc HASHCODE
  17. #HPHB @OlivierBourgain & @OlivierCroisier Contrat • Sous-ensemble de l'identité •

    Cohérence avec equals() • BankAccount (id, balance) equals() : id hashCode() : id + balance • account(42, 1000€) → groupe A account(42, 2000€) → groupe B ! HASHCODE
  18. #HPHB @OlivierBourgain & @OlivierCroisier Cas du hashcode constant HASHCODE G

    D B I E #42 H C F A contains( ) ? C hashCode( ) = 42 C .equals( , , , , , , ...) ? F C A C D E H I
  19. #HPHB @OlivierBourgain & @OlivierCroisier Guide d'implémentation (Java 7+) • java.util.Objects.hashCode()

    • java.util.Objects.hash(Object… fields) HASHCODE public int hashCode() { return Objects.hash(bankId, accountId); }
  20. #HPHB @OlivierBourgain & @OlivierCroisier Guide d'implémentation (Java 7+) HASHCODE Arrays#hashCode

    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; }
  21. #HPHB @OlivierBourgain & @OlivierCroisier Guide d'implémentation (autres) • Génération par

    un IDE ou une librairie • Bien choisir les champs utilisés • Attention aux performances • Apache Commons ReflectionHashCode HASHCODE
  22. #HPHB @OlivierBourgain & @OlivierCroisier Dans le JDK • Collections Hash*

    • 2 types de structures • En buckets (HashMap) • Open addressing (IdentityHashMap) EN ACTION
  23. #HPHB @OlivierBourgain & @OlivierCroisier HashMap • Structure interne : tableau

    de buckets • Bucket de destination • Re-hash & bit-shifting int reHash = 0; if (key != null) { int hash = key.hashCode(); reHash = hash ^ (hash >>> 16); } int pos = (size - 1) & reHash; EN ACTION
  24. #HPHB @OlivierBourgain & @OlivierCroisier HashMap • Structure d'un bucket •

    0-7 éléments : liste chaînée (Nodes) • 8+ éléments : arbre binaire (TreeNodes) • Transformation bi-directionnelle EN ACTION
  25. #HPHB @OlivierBourgain & @OlivierCroisier EN ACTION

  26. #HPHB @OlivierBourgain & @OlivierCroisier HashMap • 16 buckets initiaux •

    Load factor 0.75 • Redimensionnement • Taille x 2 • Re-hachage EN ACTION
  27. #HPHB @OlivierBourgain & @OlivierCroisier HashMap • Performance moyenne en O(1)

    • Insertion, recherche • Dégradée en O(n) ou O(log n) • Parcours ∝ capacité EN ACTION
  28. #HPHB @OlivierBourgain & @OlivierCroisier IdentityHashMap • Distinction physique des instances

    • Egalité avec == • Hashcode avec System.identityHashCode() EN ACTION
  29. #HPHB @OlivierBourgain & @OlivierCroisier IdentityHashMap • Structure interne : tableau

    d'éléments • (clé,valeur) aux indices (N,N+1) • Indice de destination • Re-hash • Indice suivant si collision (linear probing) int hash = System.identityHashCode(element); int pos = ((hash << 1) - (hash << 8)) & (length - 1); EN ACTION
  30. #HPHB @OlivierBourgain & @OlivierCroisier IdentityHashMap K2 V2 0 1 2

    3 4 5 6 7 8 ... V1 K1 K3 V3 EN ACTION
  31. #HPHB @OlivierBourgain & @OlivierCroisier IdentityHashMap • Tableau initial de 32

    cases (16 éléments) • Load factor 0.66 • Redimensionnement • Taille x 2 • Re-hachage EN ACTION
  32. #HPHB @OlivierBourgain & @OlivierCroisier IdentityHashMap • Performance en O(1) •

    Insertion, recherche • Dégradée en O(n) • Parcours ∝ capacité EN ACTION
  33. #HPHB @OlivierBourgain & @OlivierCroisier Performances des tables de hachage •

    Directement liées à la qualité de la fonction • Fonction idéale • Dispersion importante • Très rapide à calculer • A éviter : constante PERFORMANCES
  34. #HPHB @OlivierBourgain & @OlivierCroisier Dispersion • Insertion de 2M Strings

    dans un HashSet chars time(s) 0 12046 1 828 2 247 3 122 4 92 5 82 6 75 PERFORMANCES
  35. #HPHB @OlivierBourgain & @OlivierCroisier Hashcode par défaut • Object.hashCode() •

    Méthode native • Mythe : Adresse mémoire ? • 6 algorithmes dans OpenJDK 8 • -XX:hashCode={0-5} • Par défaut : -XX:hashCode=5 PERFORMANCES
  36. #HPHB @OlivierBourgain & @OlivierCroisier Hashcode par défaut • 0 :

    Random • 1 : Manipulation des bits de l'adresse xor random • 2 : Constante : 1 • 3 : Séquence • 4 : Adresse mémoire • 5 : Marsaglia xor-shift PERFORMANCES
  37. #HPHB @OlivierBourgain & @OlivierCroisier Hashcode par défaut • Est écrit

    dans le header de l'objet de manière thread-safe • A des impacts sur la synchronisation • Biased locking • System.identityHashCode() PERFORMANCES
  38. #HPHB @OlivierBourgain & @OlivierCroisier Pré-dimensionnement • Minimise les collisions •

    Dégradation ∝ taux de collision • Evite le redimensionnement • Garbage • Re-hachage PERFORMANCES
  39. #HPHB @OlivierBourgain & @OlivierCroisier Pré-dimensionnement • Estimer par excès •

    Eviter le redimensionnement • Tenir compte des implémentations • Load factor • Alignement aux puissances de 2 PERFORMANCES
  40. #HPHB @OlivierBourgain & @OlivierCroisier Pré-dimensionnement PERFORMANCES 24576 196608 786432 3145728

    0 10 20 30 40 50 60 70 80 90 n n+1 nombre d'insertions durée (ms)
  41. #HPHB @OlivierBourgain & @OlivierCroisier Pré-dimensionnement PERFORMANCES 24577 196609 786433 3145729

    0 10 20 30 40 50 60 70 80 90 redim. sans redim. nombre d'insertions durée (ms)
  42. #HPHB @OlivierBourgain & @OlivierCroisier Résolution des collisions • Chaînage •

    Avec ou sans nœud initial • Open addressing • Linear / quadratic probing • Double hashing • Cuckoo hashing... PERFORMANCES
  43. #HPHB @OlivierBourgain & @OlivierCroisier Conclusion • Les méthodes equals() et

    hashCode() sont importantes • Identité métier • Performance et cohérence des collections • Respectez les contrats • Etudiez les structures de données • Dans le JDK et en-dehors CONCLUSION
  44. #HPHB @OlivierBourgain & @OlivierCroisier DEVOXX FRANCE 2015 Hashons peu, mais

    hashons bien Questions ? Olivier BOURGAIN Freelance OBMG @OlivierBourgain Olivier CROISIER Freelance Moka Technologies @OlivierCroisier