Bir Java HashMap aynı karma koduna sahip farklı nesneleri nasıl işler?


223

Anladığım kadarıyla düşünüyorum:

  1. İki nesnenin aynı karma koduna sahip olması tamamen yasaldır.
  2. İki nesne eşitse (equals () yöntemini kullanarak) aynı karma koduna sahiptirler.
  3. İki nesne eşit değilse aynı hashcode'a sahip olamazlar

Doğrumuyum?

Şimdi doğru ise, aşağıdaki soru var: HashMapDahili nesnenin hashcode kullanır. Eğer iki nesne aynı hashcode'a sahipse, HashMaphangi anahtarı kullandığını nasıl takip edebilir ?

Birisi HashMapnesnenin hash kodunu dahili olarak nasıl kullandığını açıklayabilir mi?


29
Kayıt için: # 1 ve # 2 doğru, # 3 yanlış: Eşit olmayan iki nesne aynı karma koduna sahip olabilir .
Joachim Sauer

6
# 1 ve # 3 bile çelişkilidir
Delfic

Gerçekten de, # 2 takip edilmezse, equals () uygulaması (veya muhtemelen hashCode ()) yanlıştır.
Joachim Sauer

Yanıtlar:


346

Bir hashmap böyle çalışır (bu biraz basitleştirilmiştir, ancak temel mekanizmayı göstermektedir):

Anahtar / değer çiftlerini saklamak için kullandığı bir dizi "kova" vardır. Her bir grubun benzersiz bir numarası vardır - bu grubu tanımlayan şeydir. Bir anahtar / değer çiftini haritaya koyduğunuzda, hashmap anahtarın hash koduna bakar ve çifti tanımlayıcının anahtarın hash kodu olduğu kovada saklar. Örneğin: Anahtarın karma kodu 235 -> çift 235 numaralı kovada saklanır. (Bir grubun birden fazla anahtar / değer çiftinden fazlasını depolayabileceğini unutmayın).

Bir anahtar vererek hashmap içindeki bir değeri aradığınızda, önce verdiğiniz anahtarın hash koduna bakar. Daha sonra hashmap karşılık gelen kovaya bakacak ve daha sonra verdiğiniz anahtarı kovadaki tüm çiftlerin anahtarlarıyla karşılaştırarak karşılaştıracaktır equals().

Şimdi bunun bir haritadaki anahtar / değer çiftlerini aramak için nasıl çok etkili olduğunu görebilirsiniz: anahtarın karma koduyla hashmap hangi kepçeye bakılacağını hemen bilir, böylece sadece o kovada bulunanlara karşı test etmek zorundadır.

Yukarıdaki mekanizmaya bakarak, tuşların hashCode()ve equals()yöntemlerinde hangi gereksinimlerin gerekli olduğunu da görebilirsiniz :

  • İki anahtar aynıysa ( bunları karşılaştırdığınızda equals()döndürür true), hashCode()yöntemleri aynı sayıyı döndürmelidir. Anahtarlar bunu ihlal ederse, eşit olan anahtarlar farklı kovalarda saklanabilir ve hashmap anahtar / değer çiftlerini bulamaz (çünkü aynı grupta görünecektir).

  • İki anahtar farklıysa, karma kodlarının aynı olup olmadığı önemli değildir. Karma kodları aynıysa aynı kovada saklanırlar ve bu durumda hashmap equals()bunları ayırmak için kullanır.


4
"ve hashmap anahtar / değer çiftlerini bulamazdı (çünkü aynı grupta görünecek)." Aynı kovaya bakacağınızı açıklayabilir misiniz, bu iki eşit nesne t1 ve t2 ve eşittir ve t1 ve t2 sırasıyla h1 ve h2 hashcode'larına sahiptir. Yani t1.equals (t2) = true ve h1! = H2 Peki, hashmap t1'i aradığında, h1 kepçesinde ve t2 kepçesinde t2'yi arayacak mı?
Geek

19
İki anahtar eşitse ancak hashCode()yöntemleri farklı karma kodları döndürüyorsa , anahtar sınıfının equals()ve hashCode()yöntemleri sözleşmeyi ihlal eder ve bu anahtarları a'da kullanırken garip sonuçlar alırsınız HashMap.
Jesper

Her grup, bağlantılı listeyi dahili olarak kullanan birden çok Anahtar Değer çifti çiftine sahip olabilir. Ama karışıklığım - burada kova nedir? Dahili olarak hangi veri yapısını kullanıyor? Kovalar arasında herhangi bir bağlantı var mı?
Ankit Sharma

1
@AnkitSharma Tüm ayrıntıları gerçekten bilmek istiyorsanız , JDK kurulum dizininizdeki HashMapdosyada bulabileceğiniz kaynak kodunu arayın src.zip.
Jesper

1
@ 1290 Aynı gruptaki anahtarlar arasındaki tek ilişki, aynı karma koduna sahip olmalarıdır.
Jesper

88

Üçüncü iddianız yanlış.

İki eşit olmayan nesnenin aynı karma koduna sahip olması tamamen yasaldır. Tarafından kullanılırHashMapHaritanın belirtilen anahtarla olası girişleri hızlı bir şekilde bulabilmesi için "ilk geçiş filtresi" olarak . Aynı karma koduna sahip anahtarlar, belirtilen anahtarla eşitlik açısından test edilir.

İki eşit olmayan nesnenin aynı karma koduna sahip olamayacağı, aksi takdirde sizi 2 32 olası nesne ile sınırlandıracak bir gereksinim istemezsiniz . (Diğer sınıflar aynı hash üretebileceğinden, farklı türlerin bir nesnenin alanlarını karma kodları oluşturmak için bile kullanamayacağı anlamına gelir.)


6
2 ^ 32 olası nesneye nasıl ulaştınız?
Geek

5
Geç kaldım, ama hala merak edenler için: Java'daki bir hashcode bir int ve int bir 2 ^ 32 olası değere sahip
Xerus

69

HashMap yapı diyagramı

HashMap bir dizi Entry nesne .

Düşünmek HashMapYalnızca bir nesne dizisi olarak .

Bunun ne olduğuna bir göz atın Object:

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;
 
}

Her Entrynesne bir anahtar / değer çiftini temsil eder. Bir kovada birden fazla varsa alan nextbaşka bir Entrynesneye başvururEntry .

Bazen 2 farklı nesne için karma kodlar aynı olabilir. Bu durumda, iki nesne bir kovaya kaydedilir ve bağlantılı liste olarak sunulur. Giriş noktası daha yakın zamanda eklenen nesnedir. Bu nesne, nextalanı olan başka bir nesneyi ifade eder . Son giriş,null .

HashMapVarsayılan yapıcı ile bir oluşturduğunuzda

HashMap hashMap = new HashMap();

Dizi 16 boyut ve varsayılan 0.75 yük dengesi ile oluşturulur.

Yeni bir anahtar / değer çifti ekleme

  1. Anahtar için karma kodunu hesapla
  2. Konumu hesapla hash % (arrayLength-1)Elemanın yerleştirileceği (kova numarası)
  3. Daha önce kaydedilmiş bir anahtarla değer eklemeye çalışırsanız HashMap , değerin üzerine yazılır.
  4. Aksi takdirde kovaya eleman eklenir.

Kovada zaten en az bir öğe varsa, yeni bir tane eklenir ve kovanın ilk konumuna yerleştirilir. Onunnext eski öğeyi ifade eder.

silme

  1. Verilen anahtar için hashcode'u hesapla
  2. Grup numarasını hesapla hash % (arrayLength-1)
  3. Gruptaki ilk Giriş nesnesine bir başvuru alın ve eşit grup yöntemiyle verilen gruptaki tüm girdiler üzerinde yineleme yapın. Sonunda doğru olanı bulacağız Entry. İstediğiniz bir öğe bulunamazsa, geri dönünnull

3
Bu yanlış hash % (arrayLength-1)olur hash % arrayLength. Ama aslında öyle hash & (arrayLength-1) . Yani, 2^ndizi uzunluğu için nen az önemli bitler alarak iki ( ) gücü kullanır .
weston

Bence Entity nesnelerinin bir dizi değil, LinkedList / Tree bir dizi. Ve her ağacın dahili olarak Entity nesneleri vardır.
Mudit bhaintwal

@shevchyk neden anahtar ve karma depolarız? kullanımları nelerdir? Burada hafızayı boşa harcamıyor muyuz?
roottraveller

hashset dahili olarak hashmap kullanır. ekleme ve silme kuralları hashset için iyi tutar mı?
17'de

2
@weston sadece bu değil, hashCode bir intnegatif olabilir, negatif bir sayı üzerinde modulo yapmak size negatif bir sayı verecektir
Eugene

35

Mükemmel bilgileri http://javarevisited.blogspot.com/2011/02/how-hashmap-works-in-java.html adresinde bulabilirsiniz.

Özetlemek:

HashMap karma prensibi üzerine çalışır

put (anahtar, değer): HashMap hem anahtar hem de değer nesnesini Map.Entry olarak depolar. Hashmap, grubu almak için hashcode (anahtar) uygular. çarpışma varsa, HashMap nesneyi saklamak için LinkedList kullanır.

get (key): HashMap, kova konumunu bulmak için Anahtar Nesnenin hashcode'unu kullanır ve ardından LinkedList'teki doğru düğümü tanımlamak ve Java HashMap'te o anahtar için ilişkili değer nesnesini döndürmek için keys.equals () yöntemini çağırır.


3
Jasper'ın verdiği cevabı daha iyi buldum, blogun konsepti anlamaktan çok röportajı ele almakta olduğunu hissettim
Narendra N

@NarendraN Sana katılıyorum.
Abhijit Gaikwad

22

İşte sürümü HashMapiçin 'mekanizmasının kaba bir açıklaması ( Java 6'dan biraz farklı olabilir) .Java 8


Veri yapıları

  • Karma tablosu
    Karma değeri, hash()anahtar üzerinden hesaplanır ve belirli bir anahtar için karma tablonun hangi grubunun kullanılacağına karar verir.
  • Bağlantılı liste (tek başına)
    Bir gruptaki öğelerin sayısı az olduğunda, tekli bağlantılı bir liste kullanılır.
  • Kırmızı-Siyah ağaç
    Bir kovadaki eleman sayısı büyük olduğunda, kırmızı-siyah bir ağaç kullanılır.

Sınıflar (dahili)

  • Map.Entry
    Haritadaki tek bir varlığı, anahtar / değer varlığını temsil eder.
  • HashMap.Node
    Düğümün bağlantılı liste sürümü.

    Şunları temsil edebilir:

    • Bir karma kova.
      Çünkü hash özelliği vardır.
    • Tek başına bağlantılı listedeki bir düğüm, (bu nedenle aynı zamanda bağlantılı listenin başı) .
  • HashMap.TreeNode
    Düğümün ağaç sürümü.

Alanlar (dahili)

  • Node[] table
    Grup tablosu, (bağlantılı listelerin başı).
    Bir kova öğe içermiyorsa, boştur, bu nedenle yalnızca bir referans alanı kaplar.
  • Set<Map.Entry> entrySet Varlıkları kümesi.
  • int size
    Varlık sayısı.
  • float loadFactor
    Yeniden boyutlandırmadan önce karma tablosunun ne kadar dolu olduğuna izin verin.
  • int threshold
    Yeniden boyutlandırılacak bir sonraki boyut.
    Formül:threshold = capacity * loadFactor

Yöntemler (dahili)

  • int hash(key)
    Hash anahtarını hesaplayın.
  • Hash kepçe ile nasıl eşleştirilir?
    Aşağıdaki mantığı kullanın:

    static int hashToBucket(int tableSize, int hash) {
        return (tableSize - 1) & hash;
    }

Kapasite hakkında

Karma tabloda, kapasite, kova sayısı anlamına gelir, buradan alınabilir table.length.
Ayrıca thresholdve üzerinden hesaplanabilir loadFactor, bu nedenle bir sınıf alanı olarak tanımlanmasına gerek yoktur.

Etkili kapasite aşağıdakiler yoluyla elde edilebilir: capacity()


Operasyonlar

  • Varlığı anahtarla bulun.
    Önce grubu karma değerine göre bulun, ardından bağlantılı listeyi döngüye sokun veya sıralanmış ağacı arayın.
  • Varlığı anahtarla ekleyin.
    Önce kepçeyi anahtarın karma değerine göre bulun.
    Ardından değeri bulmayı deneyin:
    • Eğer bulunursa, değeri değiştirin.
    • Aksi takdirde, bağlı listenin başına yeni bir düğüm ekleyin veya sıralanan ağaca ekleyin.
  • Yeniden boyutlandırma
    zaman thresholdulaşılan Hashtable'a kapasitesini (katına çıkacak, table.length), sonra tabloyu yeniden inşa etmek için tüm unsurları yeniden karma gerçekleştirin.
    Bu pahalı bir ameliyat olabilir.

Verim

  • get & put
    Zaman karmaşıklığı O(1), çünkü:
    • Kovaya dizi indeksi üzerinden erişilir O(1).
    • Her gruptaki bağlantılı liste küçük uzunluktadır, dolayısıyla O(1) .
    • Kapasitesini ve yeniden karma elemanı sayısındaki artışın uzatacaktır çünkü Ağacı boyutu da böylece görüntüleyebilmek, sınırlı O(1)değil O(log N).

Lütfen örnek verebilir misiniz Zaman karmaşıklığı vardır O (1)
Jitendra

@jsroyal Bu karmaşıklığı daha açık bir şekilde açıklayabilir: en.wikipedia.org/wiki/Hash_table . Ancak kısaca: hedef dizeyi bulmak O (1) 'dir, çünkü dizideki dizine göre bulursunuz; daha sonra bir kova içinde, miktar küçüktür ve tüm karmadaki toplam eleman sayısına rağmen ortalama olarak sabit bir sayıdır, bu nedenle bir kova içindeki hedef elemanın aranması da O (1) 'dir; bu nedenle O (1) + O (1) = O (1).
Eric Wang

14

Hashcode, hashrap için hangi kovayı kontrol edeceğini belirler. Grupta birden fazla nesne varsa, gruptaki hangi öğenin istenen öğeye ( equals()) yöntemini kullanarak eşit olduğunu bulmak için doğrusal bir arama yapılır .

Başka bir deyişle, mükemmel bir karma kodunuz varsa hashmap erişimi sabitse, hiçbir zaman bir kova boyunca yineleme yapmanız gerekmez (teknik olarak MAX_INT kovalara da sahip olmanız gerekir, Java uygulaması aynı kovada birkaç karma kodu paylaşabilir alan gereksinimlerini azaltır). En kötü hashcode'unuz varsa (her zaman aynı numarayı döndürür), istediğiniz şeyi elde etmek için haritadaki her öğeyi (hepsi aynı gruptadır) aramak zorunda olduğunuzdan hashmap erişiminiz doğrusal hale gelir.

Çoğu zaman iyi yazılmış bir hashcode mükemmel değildir, ancak size daha fazla veya daha az sürekli erişim sağlayacak kadar benzersizdir.


11

Üçüncü noktada yanılıyorsun. İki giriş aynı karma koduna sahip olabilir, ancak eşit olamaz. OpenJdk'den HashMap.get uygulamasına bir göz atın . Karmanın eşit olduğunu ve anahtarların eşit olduğunu kontrol ettiğini görebilirsiniz. Üçüncü nokta doğru olsaydı, o zaman tuşların eşit olup olmadığını kontrol etmek gereksiz olurdu. Hash kodu anahtardan önce karşılaştırılır, çünkü önceki kod daha verimli bir karşılaştırmadır.

Bu konuda biraz daha fazla bilgi edinmek istiyorsanız , OpenJDk uygulamasının kullandığı mekanizma olduğuna inandığım Açık Adresleme çarpışma çözümü hakkındaki Wikipedia makalesine bakın . Bu mekanizma, diğer cevaplardan biri olan "kova" yaklaşımından ince bir şekilde farklıdır.


6
import java.util.HashMap;

public class Students  {
    String name;
    int age;

    Students(String name, int age ){
        this.name = name;
        this.age=age;
    }

    @Override
    public int hashCode() {
        System.out.println("__hash__");
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        System.out.println("__eq__");
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Students other = (Students) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    public static void main(String[] args) {

        Students S1 = new Students("taj",22);
        Students S2 = new Students("taj",21);

        System.out.println(S1.hashCode());
        System.out.println(S2.hashCode());

        HashMap<Students,String > HM = new HashMap<Students,String > (); 
        HM.put(S1, "tajinder");
        HM.put(S2, "tajinder");
        System.out.println(HM.size());
    }
}

Output:

__ hash __

116232

__ hash __

116201

__ hash __

__ hash __

2

Burada, hem S1 hem de S2 nesneleri farklı içeriğe sahipse, geçersiz kılınan Hashcode yöntemimizin her iki nesne için farklı Hashcode (116232,11601) üreteceğinden eminiz. ŞİMDİ farklı karma kodlar olduğundan, EQUALS yöntemini çağırmak bile rahatsız olmaz. Çünkü farklı bir Hashcode bir nesnedeki FARKLI içeriği GARANTİ EDİYOR.

    public static void main(String[] args) {

        Students S1 = new Students("taj",21);
        Students S2 = new Students("taj",21);

        System.out.println(S1.hashCode());
        System.out.println(S2.hashCode());

        HashMap<Students,String > HM = new HashMap<Students,String > (); 
        HM.put(S1, "tajinder");
        HM.put(S2, "tajinder");
        System.out.println(HM.size());
    }
}

Now lets change out main method a little bit. Output after this change is 

__ hash __

116201

__ hash __

116201

__ hash __

__ hash __

__ eq __

1
We can clearly see that equal method is called. Here is print statement __eq__, since we have same hashcode, then content of objects MAY or MAY not be similar. So program internally  calls Equal method to verify this. 


Conclusion 
If hashcode is different , equal method will not get called. 
if hashcode is same, equal method will get called.

Thanks , hope it helps. 

3

iki nesne eşittir, aynı hash koduna sahip oldukları anlamına gelir, ancak tam tersi değildir.

2 eşit nesne ------> aynı hash koduna sahipler

2 nesnenin aynı karma kodu vardır ---- xxxxx -> eşit DEĞİLDİR

HashMap'te Java 8 güncellemesi

bu işlemi kodunuzda yaparsınız -

myHashmap.put("old","old-value");
myHashMap.put("very-old","very-old-value");

bu nedenle, her iki anahtar için de hashcode'unuzun döndüğünü "old"ve "very-old"aynı olduğunu varsayalım . Sonra ne olacak.

myHashMapbir HashMap ve başlangıçta kapasitesini belirtmediğinizi varsayalım. Java'ya göre varsayılan kapasite 16'dır. Şimdi yeni anahtar kelimeyi kullanarak hashmap'i başlattığınızda, 16 kova oluşturdu. şimdi ilk ifadeyi yaptığınızda

myHashmap.put("old","old-value");

sonra için hashcode "old"hesaplanır ve hashcode da çok büyük bir tamsayı olabileceğinden, java dahili olarak bunu yaptı - (hash burada hashcode ve >>> sağ kaydırma)

hash XOR hash >>> 16

daha büyük bir resim olarak vermek için, 0 ile 15 arasında bir indeks döndürür. Şimdi anahtar değer çiftiniz "old"ve"old-value" Giriş nesnesinin anahtar ve değer örneği değişkenine dönüştürülecektir. ve sonra bu giriş nesnesi grupta saklanır veya belirli bir dizinde bu giriş nesnesinin saklanacağını söyleyebilirsiniz.

FYI- Entry, Map arabiriminde Map.Entry'de bu imzayla / tanımla bir sınıftır

class Entry{
          final Key k;
          value v;
          final int hash;
          Entry next;
}

şimdi bir sonraki ifadeyi çalıştırdığınızda -

myHashmap.put("very-old","very-old-value");

ve "very-old"aynı hash kodunu verir "old", böylece bu yeni anahtar / değer çifti tekrar aynı dizine veya aynı gruba gönderilir. Ancak bu grup boş olmadığından next, Entry nesnesinin değişkeni bu yeni anahtar / değer çiftini saklamak için kullanılır.

ve bu, aynı hash koduna sahip her nesne için bağlantılı liste olarak saklanır, ancak 6 değeriyle bir TRIEFY_THRESHOLD değeri belirtilir, böylece bu ulaşıldıktan sonra, bağlantılı liste ilk öğe olarak dengeli ağaca (kırmızı-siyah ağaç) dönüştürülür. kök.


harika cevap (y)
Sudhanshu Gaur

2

Her Entry nesnesi anahtar / değer çiftini temsil eder. Bir kovada birden fazla Giriş varsa, sonraki alan diğer Giriş nesnesini ifade eder.

Bazen 2 farklı nesne için hashCode'ları aynı olabilir. Bu durumda 2 nesne bir kovaya kaydedilir ve LinkedList olarak sunulur. Giriş noktası daha yakın zamanda eklenen bir nesnedir. Bu nesne, bir sonraki alana ve diğer bir nesneye sahip olan diğer nesneyi ifade eder. Son giriş null değerini belirtir. Varsayılan yapıcı ile HashMap oluşturduğunuzda

Dizi, boyut 16 ve varsayılan 0.75 yük dengesi ile oluşturulur.

resim açıklamasını buraya girin

(Kaynak)


1

Hash haritası karma prensibi üzerine çalışır

HashMap get (Key k) yöntemi, anahtar nesnesinde hashCode yöntemini çağırır ve anahtarların ve değerlerin Entry (Map) adlı iç içe bir sınıf biçiminde saklandığı bir kova konumunu (yedekleme dizisi) bulmak için kendi hashValue değerini kendi statik karma işlevine uygular. Giriş). Böylece, önceki satırdan hem anahtar hem de değerin grupta bir Giriş nesnesi biçimi olarak saklandığı sonucuna vardınız. Bu nedenle, kovada sadece değerin depolandığını düşünmek doğru değildir ve görüşmeci üzerinde iyi bir izlenim bırakmayacaktır.

  • HashMap nesnesinde get (Key k) yöntemini her çağırdığımızda. İlk olarak anahtarın boş olup olmadığını kontrol eder. HashMap'te yalnızca bir boş anahtar olabileceğini unutmayın.

Anahtar null ise, Null anahtarlar her zaman hash 0 ile eşlenir, böylece dizin 0 olur.

Anahtar null değilse, anahtar nesnesinde hashfunction işlevini çağıracaktır, yukarıdaki yöntemin 4. satırına bakınız.

            int hash = hash(hashValue)

ve şimdi, döndürülen hashValue değerini kendi hashing işlevine uygular.

Neden hash değerini (hashValue) kullanarak tekrar hesapladığımızı merak edebiliriz. Cevap: Düşük kaliteli hash fonksiyonlarına karşı koruma sağlar.

Şimdi son hashvalue, Entry nesnesinin saklandığı grup konumunu bulmak için kullanılır. Giriş nesnesi grupta böyle saklanır (karma, anahtar, değer, bucketindex)


1

HashMap'in nasıl çalıştığına dair ayrıntılara girmeyeceğim, ancak HashMap'ın gerçekle ilişkilendirerek nasıl çalıştığını hatırlayabilmemiz için bir örnek vereceğim.

Anahtar, Değer, HashCode ve kova var.

Bir ara, her birini aşağıdakilerle ilişkilendireceğiz:

  • Kova -> Bir Toplum
  • HashCode -> Toplumun adresi (her zaman benzersiz)
  • Değer -> Toplumdaki Bir Ev
  • Anahtar -> Ev adresi.

Map.get (anahtar) kullanarak:

Stevie, VIP toplumunda bir villada yaşayan arkadaşının (Josse) evine gitmek istiyor, JavaLovers Society olsun. Josse'nin adresi SSN'sidir (bu herkes için farklıdır). Topluluğun adını SSN'ye dayalı olarak bulduğumuz bir dizin var. Bu dizin HashCode'u bulmak için bir algoritma olarak düşünülebilir.

  • SSN Derneği Adı
  • 92313 (Josse's) - JavaLovers
  • 13214 - Açısal JSLovers
  • 98080 - JavaLovers
  • 53808 - BiyolojiLovers

  1. Bu SSN (anahtar) önce bize Toplumun adından başka bir şey olmayan bir HashCode (dizin tablosundan) verir.
  2. Şimdi, birden fazla ev aynı toplumda olabilir, bu nedenle HashCode ortak olabilir.
  3. Diyelim ki Toplum iki ev için ortaktır, hangi evin gideceğimizi nasıl belirleyeceğiz, evet, Ev adresinden başka bir şey olmayan (SSN) anahtarını kullanarak

Map.put kullanma (anahtar, Değer)

Bu, HashCode'u bularak bu Değer için uygun bir toplum bulur ve sonra değer saklanır.

Umarım bu yardımcı olur ve değişikliklere açıktır.


0

Uzun bir cevap olacak, bir içki alıp okumaya devam edecek…

Karma, daha hızlı okunabilen ve yazılabilen bir anahtar / değer çiftini bellekte saklamakla ilgilidir. Anahtarları bir dizide ve değerleri LinkedList'te saklar.

4 anahtar değer çifti saklamak istediğimi varsayalım -

{
girl => ahhan , 
misused => Manmohan Singh , 
horsemints => guess what”, 
no => way
}

Bu yüzden anahtarları saklamak için 4 öğeden oluşan bir diziye ihtiyacımız var. Şimdi bu 4 anahtardan birini 4 dizi dizinine (0,1,2,3) nasıl eşleyebilirim?

Böylece java, ayrı anahtarların hashCode'unu bulur ve bunları belirli bir dizi dizinine eşler. Hashcode Formülleri -

1) reverse the string.

2) keep on multiplying ascii of each character with increasing power of 31 . then add the components .

3) So hashCode() of girl would be –(ascii values of  l,r,i,g are 108, 114, 105 and 103) . 

e.g. girl =  108 * 31^0  + 114 * 31^1  + 105 * 31^2 + 103 * 31^3  = 3173020

Hash ve kız !! Ne düşündüğünü biliyorum. O vahşi düete olan ilginiz sizi önemli bir şeyi özlüyor olabilir.

Java neden 31 ile çarpıyor?

Çünkü 31, 2 ^ 5 - 1 biçiminde garip bir asaldır. Ve tek başbakan Karma Çarpışma olasılığını azaltır

Şimdi bu karma kodu bir dizi dizinine nasıl eşlenir?

cevap Hash Code % (Array length -1) ,. Yani “girl”eşleştirilmiş (3173020 % 3) = 1bizim durumumuzda. dizinin ikinci öğesidir.

ve “ahhan” değeri dizi dizini 1 ile ilişkili bir LinkedList içinde saklanır.

HashCollision - hasHCodeAnahtarları bulmaya “misused”ve “horsemints”yukarıda açıklanan formülleri kullanmaya çalışırsanız, her ikisinin de bize aynı şeyi verdiğini görürsünüz 1069518484. Whooaa !! çıkarılan ders -

2 eşit nesnenin aynı hashCode'u olmalıdır, ancak hashCode eşleşirse nesneler eşitse garanti yoktur. Bu nedenle, kova 1'e “yanlış kullanılmış” ve “at binicileri” ne karşılık gelen her iki değeri de saklamalıdır (1069518484% 3).

Şimdi karma harita şu şekilde görünüyor -

Array Index 0 
Array Index 1 - LinkedIst (“ahhan , Manmohan Singh , guess what”)
Array Index 2  LinkedList (“way”)
Array Index 3  

Şimdi bazı gövde anahtarın değerini bulmaya çalışırsa “horsemints”, java hızlı bir şekilde hashCode'unu bulur, modülleştirir ve buna karşılık gelen LinkedList'te değerini aramaya başlar index 1. Bu şekilde, 4 dizi dizininin tümünü aramamıza gerek kalmaz, böylece veri erişimini daha hızlı hale getiririz.

Ama, bir saniye. LinkedList'e karşılık gelen Array indeksi 1'de 3 değer vardır, anahtar “atlılar” için hangisinin değer olduğunu nasıl bulur?

Aslında, yalan söyledim, HashMap sadece LinkedList değerleri depolar dedi.

Her iki anahtar / değer çiftini harita girişi olarak saklar. Aslında Harita buna benziyor.

Array Index 0 
Array Index 1 - LinkedIst (<”girl => ahhan”> , <” misused => Manmohan Singh”> , <”horsemints => guess what”>)
Array Index 2  LinkedList (<”no => way”>)
Array Index 3  

Artık, ArrayIndex1'e karşılık gelen LinkedList üzerinden geçiş yaparken, bu LinkedList'in her girişinin anahtarını “at binicileri” ile karşılaştırır ve bir tane bulduğunda bunun değerini döndürür.

Umarım okurken eğlenmişsinizdir :)


Bunun yanlış olduğunu düşünüyorum: "Anahtarları bir dizide saklar ve bir LinkedList değerleri."
ACV

her grup için listedeki her öğe, anahtarı ve değeri ve bir sonraki düğüme referansı içerir.
ACV

0

Söylendiği gibi, bir resim 1000 kelimeye bedeldir. Diyorum ki: bazı kodlar 1000 kelimeden daha iyidir. İşte HashMap'in kaynak kodu. Yöntemi alın:

/**
     * Implements Map.get and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @return the node, or null if none
     */
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

Böylece, "kova" yı bulmak için karma kullanılır ve ilk öğe her zaman bu kovada kontrol edilir. Değilse, o zaman equalsanahtarın bağlantılı listede gerçek öğeyi bulmak için kullanılır.

put()Yönteme bakalım :

  /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

Biraz daha karmaşıktır, ancak yeni öğenin karmaya dayalı olarak hesaplanan konuma sekmeye yerleştirildiği açıktır:

i = (n - 1) & hashburada iyeni öğenin yerleştirileceği dizin (veya "grup" dur). dizinin nboyutu tab("kovalar" dizisi).

İlk olarak, o "kova" nın ilk elemanı olarak konmaya çalışılır. Zaten bir öğe varsa, listeye yeni bir düğüm ekleyin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.