.NET'te IEqualityComparer <T> içindeki GetHashCode'un rolü nedir?


142

IEqualityComparer arabiriminin GetHashCode yönteminin rolünü anlamaya çalışıyorum.

Aşağıdaki örnek MSDN'den alınmıştır:

using System;
using System.Collections.Generic;
class Example {
    static void Main() {
        try {

            BoxEqualityComparer boxEqC = new BoxEqualityComparer();

            Dictionary<Box, String> boxes = new Dictionary<Box,
                                                string>(boxEqC);

            Box redBox = new Box(4, 3, 4);
            Box blueBox = new Box(4, 3, 4);

            boxes.Add(redBox, "red");
            boxes.Add(blueBox, "blue");

            Console.WriteLine(redBox.GetHashCode());
            Console.WriteLine(blueBox.GetHashCode());
        }
        catch (ArgumentException argEx) {

            Console.WriteLine(argEx.Message);
        }
    }
}

public class Box {
    public Box(int h, int l, int w) {
        this.Height = h;
        this.Length = l;
        this.Width = w;
    }
    public int Height { get; set; }
    public int Length { get; set; }
    public int Width { get; set; }
}

class BoxEqualityComparer : IEqualityComparer<Box> {

    public bool Equals(Box b1, Box b2) {
        if (b1.Height == b2.Height & b1.Length == b2.Length
                            & b1.Width == b2.Width) {
            return true;
        }
        else {
            return false;
        }
    }

    public int GetHashCode(Box bx) {
        int hCode = bx.Height ^ bx.Length ^ bx.Width;
        return hCode.GetHashCode();
    }
}

Eşit yöntemi uygulaması iki Box nesnesini karşılaştırmak için yeterli olmamalı mı? Çerçeveye nesneleri karşılaştırmak için kullanılan kuralı anlatıyoruz. GetHashCode neden gereklidir?

Teşekkürler.

Lucian


Bir okuyun: en.wikipedia.org/wiki/Hash_table sonra GetHashCode'un amacını daha iyi anlayıp anlamadığınızı görün.
harcama

1
Bu harika cevaba bakın: stackoverflow.com/a/3719802/136967
Mikhail

Yanıtlar:


201

Önce biraz arka plan ...

.NET'teki her nesnenin bir Eşit yöntemi ve bir GetHashCode yöntemi vardır.

Eşittir yöntemi, bir nesneyi başka bir nesneyle karşılaştırmak için kullanılır - iki nesnenin eşdeğer olup olmadığını görmek için.

GetHashCode yöntemi, nesnenin 32 bit tamsayı gösterimini oluşturur. Bir nesnenin ne kadar bilgi içerebileceğine dair bir sınır olmadığından, belirli karma kodlar birden fazla nesne tarafından paylaşılır - bu nedenle karma kodu mutlaka benzersiz değildir.

Sözlük, Ekle / Kaldır / Al işlemleri için (daha fazla veya daha az) sabit maliyetler karşılığında daha yüksek bir bellek ayak izi ticareti yapan gerçekten harika bir veri yapısıdır. Yine de yineleme için kötü bir seçimdir. Dahili olarak sözlük, değerlerin depolanabileceği bir dizi kova içerir. Bir sözlüğe bir Anahtar ve Değer eklediğinizde, Anahtar üzerinde GetHashCode yöntemi çağrılır. Döndürülen hashcode, Anahtar / Değer çiftinin saklanması gereken grubun dizinini belirlemek için kullanılır.

Değere erişmek istediğinizde, Anahtarı tekrar iletirsiniz. GetHashCode yöntemi anahtar üzerinde çağrılır ve değeri içeren kova bulunur.

Bir sözlüğün yapıcısına bir IEqualityComparer iletildiğinde, Key nesnelerindeki yöntemler yerine IEqualityComparer.Equals ve IEqualityComparer.GetHashCode yöntemleri kullanılır.

Şimdi her iki yöntemin neden gerekli olduğunu açıklamak için şu örneği düşünün:

BoxEqualityComparer boxEqC = new BoxEqualityComparer(); 

Dictionary<Box, String> boxes = new Dictionary<Box, string>(boxEqC); 

Box redBox = new Box(100, 100, 25);
Box blueBox = new Box(1000, 1000, 25);

boxes.Add(redBox, "red"); 
boxes.Add(blueBox, "blue"); 

Örneğinizde BoxEqualityComparer.GetHashCode yöntemini kullanarak, bu kutuların her ikisi de açıkça aynı nesne olmasalar bile aynı hashcode - 100 ^ 100 ^ 25 = 1000 ^ 1000 ^ 25 = 25 - sahiptir. Bu durumda aynı hashcode olmasının nedeni, ^ (bitwise exclusive-OR) operatörünü kullanmanızdır, böylece 100 ^ 100, 1000 ^ 1000 gibi sıfır bırakmayı iptal eder. İki farklı nesne aynı anahtara sahip olduğunda, buna bir çarpışma diyoruz.

Sözlüğe aynı karma kodlu iki Anahtar / Değer çifti eklediğimizde, her ikisi de aynı grupta saklanır. Bu nedenle, bir Değer almak istediğimizde, kovayı bulmak için Anahtarımızda GetHashCode yöntemi çağrılır. Grupta birden fazla değer olduğundan, sözlük, doğru olanı bulmak için Anahtarlardaki Eşittir yöntemini çağıran gruptaki tüm Anahtar / Değer çiftlerini yineler.

Gönderdiğiniz örnekte, iki kutu eşdeğerdir, bu nedenle Equals yöntemi true değerini döndürür. Bu durumda sözlükte iki özdeş Anahtar bulunur, bu nedenle bir istisna atar.

TLDR

Özet olarak, nesnenin saklandığı bir adres oluşturmak için GetHashCode yöntemi kullanılır. Yani bir sözlük aramak zorunda değildir. Sadece hashcode'u hesaplar ve o konuma atlar. Eşittir yöntemi daha iyi bir eşitlik testidir, ancak bir nesneyi bir adres alanına eşlemek için kullanılamaz.


4
Olanlar için, ^ -Operatör, bu bit şeklinde özel VEYA operatörüdür olduğunu gördüklerini merak msdn.microsoft.com/en-us/library/zkacc7k1.aspx .
R. Schreurs

2
Bunu açıkça belirtmek için: ( msdn.microsoft.com/en-us/library/ms132155.aspx ) Uygulayıcılara Notlar Eşittir yönteminin x ve y iki nesnesi için true değerini döndürmesi durumunda, döndürülen değerin sağlanması için uygulamalar gereklidir. x için GetHashCode yöntemi ile y için döndürülen değere eşit olmalıdır.
Diego Frehner

2
@DiegoFrehner - Çok haklısın. İnsanları harekete geçirebilecek başka bir şey, nesne değiştirilirse GetHashCode yönteminin değerinin değişmemesidir. Bu nedenle, nesne içindeki GetHashCode öğesinin bağımlı olduğu alanlar salt okunur olmalıdır (değişmez). Burada bir açıklama var: stackoverflow.com/a/4868940/469701
sheikhjabootie

1
@Acentric: Bir nesnenin karma kodu, eşitliği etkileyen bir şekilde mutasyona uğratılmadıkça değişmemelidir. Bir sınıf eşitliği etkileyecek şekilde mutasyona uğrayabilirse, kod sözlükte iken mutasyona uğratabilecek koda maruz kalabilecek herhangi bir örneği sözlükte saklamaktan kaçınmalıdır. Nesneyi depolayan kod bu kurala uyuyorsa, değiştirilebilir durumu yansıtan bir karma koduna sahip olmak yararlı olabilir. Çok kötü .NET her ikisi de yararlı kavramlar olduğu için devlet eşitliğini ve denkliğini daha iyi ayırt etmiyor.
supercat

3
@Acentric: Karma tablo adresleme için karma kodu kullanmanın ötesinde bile, karma kodun arkasındaki temel fikir, iki nesnenin farklı karma kodlarına sahip olduğu bilgisinin eşitsiz olduklarını ve bunları karşılaştırmaları gerekmediğini ima etmeleridir. Sonuç olarak, birçok nesnenin karma kodunun belirli bir nesnenin karma koduyla eşleşmediği bilgisi, hiçbirinin nesneye eşit olmadığı anlamına gelir. Adresleme için bir karma kodu kullanmak temel olarak farklı karma kodlarına sahip nesneleri yok saymanın bir yoludur.
supercat

9

GetHashCode , Sözlük etiketlerinde kullanılır ve içindeki nesneleri saklamak için karma oluşturur. İşte IEqualtyComparer ve GetHashCode'un neden ve nasıl kullanılacağı hakkında güzel bir makale http://dotnetperls.com/iequalitycomparer


4
Daha: Eğer karşılaştırmak gerekirse Eşittir enouf olurdu, ama Sözlükten eleman almak gerektiğinde bunu Eşit kullanarak değil, karma ile yapmak daha kolaydır .
Ash

5

Bir için mümkün olsa da Dictionary<TKey,TValue> onun sahip olduğu GetValueve benzeri yöntemler diyoruz Equalso tek varlık aranan uyup uymadığını görme her saklanan tuş üzerinde, çok yavaş olacaktır. Bunun yerine, birçok karma tabanlı koleksiyon gibi, GetHashCodeeşleşmeyen değerlerin çoğunun dikkate alınmasını hızlı bir şekilde hariç tutmaya dayanır . GetHashCodeAranan bir öğeyi çağırmak 42 verirse ve bir koleksiyonda 53.917 öğe varsa, ancakGetHashCode 42 dışında bir değer verdiyse, arananlarla yalnızca 3 madde karşılaştırılmalıdır. Diğer 53.914 güvenle göz ardı edilebilir.

Nedeni , ancak "kutu" ya da "zebra" özdeş birbirlerine "Fox" ve "FOX" gördüğü yöntem.GetHashCode bir dahildir IEqualityComparer<T>bir Sözlüğün tüketici normalde olurdu eşit nesneler olarak bağlamda isteyebileceği olasılığına izin vermektir değil eşit olarak birbirlerini görüyorlar. En yaygın örnek, dizeleri anahtar olarak kullanmak, ancak büyük / küçük harfe duyarlı olmayan karşılaştırmalar kullanmak isteyen bir arayan olabilir. Bunun verimli bir şekilde çalışmasını sağlamak için sözlüğün "Fox" ve "FOX" için aynı değeri verecek, ancak umarım "box" veya "zebra" için başka bir şey verecek bir hash fonksiyonuna sahip olması gerekir. Yerleşik GetHashCodeyöntem Stringbu şekilde çalışmadığından, sözlüğün böyle bir yöntemi başka bir yerden alması gerekir,IEqualityComparer<T>Equals


Sorunun doğru ve noktaya cevabı! GetHashCode (), söz konusu nesneler için Eşittir () öğesini tamamlamalıdır.
Sumith

@Sumith: Birçok hash tartışması kovalar hakkında konuşuyor, ancak bence dışlamayı düşünmek daha yararlı. Karşılaştırmalar pahalıysa, karma, kovalar halinde düzenlenmemiş koleksiyonları kullanırken bile fayda sağlayabilir.
supercat
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.