0Pricing
Java Academy · Ders

hashCode Uygulama

Doğru özetleme işlevleri yazın

hashCode Uygulama, CoddyKit'te ücretsiz bir Java Academy dersidir. Bu, 4 dersinin 3. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Java Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Java Academy kursu toplamda 4 dersten oluşur.

İyi Bir hashCode'un Amaçları

İyi bir hashCode() şunları yapmalıdır:

  • Eşit nesneler için aynı değeri döndürmelidir (sözleşme).
  • Eşit olmayan nesneleri birçok farklı değere yaymalıdır.
  • Hesaplanması ucuz olmalıdır.

Sabit bir değer döndüren kötü bir hashCode sözleşmeyi yine de karşılar, ancak haritayı yavaş bir bağlı listeye dönüştürür.

public class Main {
    public static void main(String[] args) {
        // Legal but terrible: every object collides
        System.out.println("constant hashCode is legal but kills performance");
    }
}

Yaygın Durum İçin Objects.hash

En basit doğru yaklaşım Objects.hash(field1, field2, ...) kullanmaktır.

Boş değerleri ele alır ve alanları standart bir algoritmayla birleştirir. equals ile karşılaştırdığınız aynı alanları kullanın.

import java.util.Objects;

public class Main {
    static class User {
        final String name; final int age;
        User(String name, int age) { this.name = name; this.age = age; }
        @Override public int hashCode() { return Objects.hash(name, age); }
    }
    public static void main(String[] args) {
        User a = new User("Ada", 36);
        User b = new User("Ada", 36);
        System.out.println(a.hashCode() == b.hashCode());
    }
}

Klasik 31 Çarpanı

Elle yazılmış bir hash için standart kalıp, biriken sonucu 31 ile çarpar ve her alanın hash'ini ekler.

31 tek bir asal sayıdır ve 31 * x, (x << 5) - x ile aynıdır; bu nedenle JVM bunu optimize edebilir.

public class Main {
    static class User {
        final String name; final int age;
        User(String name, int age) { this.name = name; this.age = age; }
        @Override public int hashCode() {
            int result = 17;
            result = 31 * result + (name == null ? 0 : name.hashCode());
            result = 31 * result + age;
            return result;
        }
    }
    public static void main(String[] args) {
        System.out.println(new User("Ada", 36).hashCode());
    }
}

İlkel Türlere Hash Uygulama

Her ilkel tür için önerilen bir hash'leme yöntemi vardır:

  • int: değerin kendisini kullanın.
  • long: (int)(value ^ (value >>> 32)).
  • boolean: 1 veya 0.
  • double: Double.hashCode(value).
public class Main {
    public static void main(String[] args) {
        long id = 4_000_000_000L;
        int longHash = (int) (id ^ (id >>> 32));
        System.out.println("long hash: " + longHash);
        System.out.println("double hash: " + Double.hashCode(3.14));
        System.out.println("bool hash: " + Boolean.hashCode(true));
    }
}

Dizilere Hash Uygulama

Bir dizi üzerinde doğrudan hashCode() çağırmayın; içeriği yerine kimliği kullanır.

Düz bir dizi için Arrays.hashCode(arr), iç içe diziler içinse Arrays.deepHashCode(arr) kullanın.

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        int[] b = {1, 2, 3};
        System.out.println("identity equal: " + (a.hashCode() == b.hashCode()));
        System.out.println("content equal: " + (Arrays.hashCode(a) == Arrays.hashCode(b)));
    }
}

equals ve hashCode'u Eşzamanlı Tutun

hashCode() içinde kullanılan alanlar, equals() içinde kullanılan alanların bir alt kümesi olmalıdır; ideal olarak tamamen aynı alanlar kullanılmalıdır.

equals, hashCode'dan daha fazla alan karşılaştırırsa iki eşit nesne yine de aynı hash'i paylaşır. Buna izin verilir. Ancak hashCode, equals'ın yok saydığı bir alanı kullanırsa sözleşmeyi bozarsınız.

import java.util.Objects;

public class Main {
    static class Coord {
        final int x, y;
        Coord(int x, int y) { this.x = x; this.y = y; }
        @Override public boolean equals(Object o) {
            return o instanceof Coord c && c.x == x && c.y == y;
        }
        @Override public int hashCode() { return Objects.hash(x, y); }
    }
    public static void main(String[] args) {
        Coord a = new Coord(3, 4), b = new Coord(3, 4);
        System.out.println(a.equals(b) && a.hashCode() == b.hashCode());
    }
}

Hash'i Önbelleğe Alma

Hash'lemesi pahalı olan değişmez nesnelerde sonucu bir alanda önbelleğe alabilirsiniz.

String bunu dahili olarak tam olarak böyle yapar. Bunu yalnızca nesne gerçekten değişmez olduğunda yapın; böylece önbelleğe alınan değer hiçbir zaman güncelliğini yitirmez.

import java.util.Objects;

public class Main {
    static final class Key {
        final String a, b;
        private int hash; // 0 until computed
        Key(String a, String b) { this.a = a; this.b = b; }
        @Override public int hashCode() {
            int h = hash;
            if (h == 0) { h = Objects.hash(a, b); hash = h; }
            return h;
        }
    }
    public static void main(String[] args) {
        Key k = new Key("x", "y");
        System.out.println(k.hashCode());
        System.out.println(k.hashCode());
    }
}

Dağılım Önemlidir

İyi dağıtılmış bir hashCode, anahtarları kovalar arasında eşit biçimde dağıtır. Bir grup nesne için farklı hash kodlarını sayalım.

Farklı değer sayısı arttıkça çakışma sayısı azalır ve harita hızlanır.

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class Main {
    record Pair(int a, int b) {}
    public static void main(String[] args) {
        Set<Integer> hashes = new HashSet<>();
        for (int i = 0; i < 100; i++) {
            hashes.add(Objects.hash(i, i * 7));
        }
        System.out.println("distinct hashes: " + hashes.size());
    }
}

Kötü Dağılım Örneği

Alanları çarpmadan toplamak çakışmalar üretir: (1,2) ve (2,1) ifadelerinin her ikisinin de hash değeri 3 olur.

31 çarpanı bu simetriyi bozar; çünkü artık sıra önemlidir.

public class Main {
    static int badHash(int a, int b) { return a + b; }
    static int goodHash(int a, int b) { return 31 * a + b; }
    public static void main(String[] args) {
        System.out.println("bad (1,2): " + badHash(1, 2) + ", (2,1): " + badHash(2, 1));
        System.out.println("good (1,2): " + goodHash(1, 2) + ", (2,1): " + goodHash(2, 1));
    }
}

Değer Türleri İçin Kayıtları Tercih Edin

Salt veri taşıyıcıları için bir record, doğru ve iyi dağıtılmış bir hashCode'u otomatik olarak üretir.

Özel anlambilime ihtiyaç duyduğunuzda veya kayıt kullanamadığınızda hashCode'u elle yazın.

public class Main {
    record Money(long cents, String currency) {}
    public static void main(String[] args) {
        Money a = new Money(1099, "USD");
        Money b = new Money(1099, "USD");
        System.out.println(a.equals(b));
        System.out.println(a.hashCode() == b.hashCode());
    }
}

Hepsini Birleştirme

Eksiksiz bir değer sınıfı: değişmez alanlar, aynı alanlardan türetilen equals ve hashCode ve düzgün bir toString.

import java.util.Objects;

public class Main {
    static final class Version {
        final int major, minor, patch;
        Version(int major, int minor, int patch) {
            this.major = major; this.minor = minor; this.patch = patch;
        }
        @Override public boolean equals(Object o) {
            return o instanceof Version v && v.major == major && v.minor == minor && v.patch == patch;
        }
        @Override public int hashCode() { return Objects.hash(major, minor, patch); }
        @Override public String toString() { return major + "." + minor + "." + patch; }
    }
    public static void main(String[] args) {
        Version v = new Version(2, 1, 0);
        System.out.println(v + " hash=" + v.hashCode());
    }
}

Hızlı Kontrol

hashCode becerilerinizi test edin.

Özet

hashCode'u doğru biçimde uygulamayı öğrendiniz:

  • Yaygın durum için Objects.hash(...) kullanın.
  • Elle yazılmış hash'leme için 31 çarpanı kalıbını kullanın.
  • Dizilere varsayılan yöntemle değil, Arrays.hashCode ile hash uygulayın.
  • hashCode alanlarını equals ile eşzamanlı tutun ve kayıtları tercih edin.

Sırada, Java 8 ve sonrasında aşırı dolu kovaların nasıl ağaç yapısına dönüştürüldüğünü göreceksiniz.

import java.util.Objects;

public class Main {
    public static void main(String[] args) {
        System.out.println("hashCode recap done: " + Objects.hash("done"));
    }
}

Sıkça Sorulan Sorular

“hashCode Uygulama” dersi ücretsiz mi?

Evet — “hashCode Uygulama” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Java Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Java Academy kursu toplamda 4 dersten oluşur.

“hashCode Uygulama” dersinde ne öğreneceğim?

Doğru özetleme işlevleri yazın Java Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

Java Academy öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te Java Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 3. dersidir.

“hashCode Uygulama” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu Java Academy dersinde kod yazıp çalıştırabilir miyim?

Evet. Her Java Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. HashMap Nasıl Çalışır
  2. equals/hashCode Sözleşmesi
  3. hashCode Uygulama
  4. Ağaçlaştırma ve Performans
← Java Academy Sayfasına Dön