GetHashCode มีบทบาทอย่างไรใน IEqualityComparer <T> ใน. NET


142

ฉันพยายามที่จะเข้าใจบทบาทของวิธี GetHashCode ของอินเตอร์เฟส IEqualityComparer

ตัวอย่างต่อไปนี้นำมาจาก MSDN:

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();
    }
}

การใช้วิธี Equals ไม่ควรเพียงพอที่จะเปรียบเทียบวัตถุ Box สองตัว นั่นคือที่ที่เราบอกกรอบการทำงานกฎที่ใช้ในการเปรียบเทียบวัตถุ ทำไม GetHashCode จึงจำเป็น

ขอบคุณ

ลูเชีย


อ่าน: en.wikipedia.org/wiki/Hash_tableจากนั้นดูว่าคุณเข้าใจวัตถุประสงค์ของ GetHashCode หรือไม่
อะไรต่อมิอะไร

1
ดูคำตอบที่ยอดเยี่ยมนี้: stackoverflow.com/a/3719802/136967
Mikhail

คำตอบ:


201

พื้นหลังเล็กน้อยก่อน ...

ทุกวัตถุใน. NET มีวิธีเท่ากับและวิธี GetHashCode

วิธี Equals ใช้สำหรับเปรียบเทียบวัตถุหนึ่งกับวัตถุอื่น - เพื่อดูว่าวัตถุสองรายการนั้นเทียบเท่ากันหรือไม่

กระบวนการ GetHashCode วิธีสร้างตัวแทนจำนวน 32 บิตของวัตถุ เนื่องจากไม่มีการ จำกัด จำนวนข้อมูลที่วัตถุสามารถมีได้รหัสแฮชบางรายการจะถูกใช้งานร่วมกันโดยหลายวัตถุดังนั้นรหัสแฮชจึงไม่จำเป็นต้องซ้ำกัน

พจนานุกรมเป็นโครงสร้างข้อมูลที่ยอดเยี่ยมที่ซื้อขายหน่วยความจำที่สูงกว่าเพื่อแลกกับต้นทุนคงที่ (มากหรือน้อย) สำหรับการเพิ่ม / ลบ / รับ มันเป็นตัวเลือกที่แย่สำหรับการทำซ้ำ ภายในพจนานุกรมมีอาร์เรย์ของที่เก็บซึ่งสามารถเก็บค่าได้ เมื่อคุณเพิ่มคีย์และค่าลงในพจนานุกรมเมธอด GetHashCode จะถูกเรียกบนคีย์ hashcode ที่ส่งคืนถูกใช้เพื่อกำหนดดัชนีของที่ฝากข้อมูลที่ควรเก็บคู่คีย์ / ค่า

เมื่อคุณต้องการเข้าถึงคุณค่าคุณจะต้องผ่านรหัสอีกครั้ง กระบวนการ GetHashCode วิธีการเรียกบนคีย์และฝากข้อมูลที่ประกอบด้วยค่าอยู่

เมื่อ IEqualityComparer ถูกส่งผ่านไปยังตัวสร้างของพจนานุกรม IEqualityComparer.Equals และ IEqualityComparer.GetHashCode วิธีการจะใช้แทนวิธีการในวัตถุที่สำคัญ

ตอนนี้เพื่ออธิบายสาเหตุที่จำเป็นต้องใช้ทั้งสองวิธีพิจารณาตัวอย่างนี้:

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"); 

การใช้วิธี BoxEqualityComparer.GetHashCode ในตัวอย่างของคุณกล่องทั้งสองนี้มีแฮชโค้ดเดียวกัน - 100 ^ 100 ^ 25 = 1000 ^ 1000 ^ 25 = 25 - แม้ว่าพวกเขาจะไม่ใช่วัตถุเดียวกันอย่างชัดเจน สาเหตุที่เป็นรหัสแฮชเดียวกันในกรณีนี้เนื่องจากคุณใช้ตัวดำเนินการ ^ (bitwise exclusive-OR) เพื่อให้ 100 ^ 100 ยกเลิกการออกจากศูนย์เช่นเดียวกับ 1,000 ^ 1000 เมื่อวัตถุสองชนิดที่แตกต่างกันมีรหัสเดียวกันเราเรียกสิ่งนั้นว่าการชนกัน

เมื่อเราเพิ่มคู่คีย์ / ค่าที่มีแฮชโค้ดเดียวกันลงในพจนานุกรมทั้งคู่จะถูกเก็บไว้ในที่เก็บข้อมูลเดียวกัน ดังนั้นเมื่อเราต้องการดึงค่าเมธอด GetHashCode จะถูกเรียกบนคีย์ของเราเพื่อค้นหาที่เก็บข้อมูล เนื่องจากมีค่ามากกว่าหนึ่งค่าในที่เก็บข้อมูลพจนานุกรมจะวนซ้ำค่าคู่คีย์ / ค่าทั้งหมดในที่ฝากข้อมูลที่เรียกวิธีการเท่ากับบนคีย์เพื่อค้นหาค่าที่ถูกต้อง

ในตัวอย่างที่คุณโพสต์กล่องสองกล่องนั้นเท่ากันดังนั้นเมธอด Equals จะส่งกลับค่าจริง ในกรณีนี้พจนานุกรมมีคีย์เหมือนกันสองปุ่มดังนั้นจึงมีข้อผิดพลาด

TLDR

ดังนั้นโดยสรุปเมธอด GetHashCode จะใช้เพื่อสร้างที่อยู่ที่เก็บวัตถุ ดังนั้นพจนานุกรมไม่จำเป็นต้องค้นหา มันแค่คำนวณแฮชโค้ดและข้ามไปยังตำแหน่งนั้น วิธี Equals เป็นการทดสอบความเท่าเทียมที่ดีกว่า แต่ไม่สามารถใช้ในการแมปวัตถุเข้ากับพื้นที่ที่อยู่


4
สำหรับผู้ที่สงสัยว่าเป็น ^ -operator นี้เป็นบิตพิเศษหรือผู้ประกอบการเห็นmsdn.microsoft.com/en-us/library/zkacc7k1.aspx
R. Schreurs

2
เพื่อชี้ให้เห็นสิ่งนี้อย่างชัดเจน: ( msdn.microsoft.com/en-us/library/ms132155.aspx ) หมายเหตุสำหรับ Implementers Implementers จำเป็นต้องมีเพื่อให้แน่ใจว่าหากเมธอด Equals ส่งคืนค่าจริงสำหรับสองออบเจ็กต์ x และ y ดังนั้นค่าที่ส่งคืน โดย GetHashCode วิธีการสำหรับ x ต้องเท่ากับค่าที่ส่งคืนสำหรับ y
Diego Frehner

2
@DiegoFrehner - คุณพูดถูก อีกสิ่งที่สามารถทำให้ผู้คนเดินทางไปได้นั่นคือคุณค่าของเมธอด GetHashCode ไม่ควรเปลี่ยนแปลงหากมีการแก้ไขวัตถุ ดังนั้นเขตข้อมูลภายในวัตถุที่ GetHashCode ขึ้นอยู่กับควรอ่านได้อย่างเดียว (ไม่เปลี่ยนรูป) มีคำอธิบายที่นี่: stackoverflow.com/a/4868940/469701
sheikhjabootie

1
@Acentric: รหัสแฮชของวัตถุไม่ควรเปลี่ยนแปลงจนกว่าจะมีการเปลี่ยนแปลงในรูปแบบที่มีผลต่อความเท่าเทียมกัน หากคลาสสามารถกลายพันธุ์ในลักษณะที่จะส่งผลกระทบต่อความเท่าเทียมกันโค้ดควรหลีกเลี่ยงการจัดเก็บในพจนานุกรมใด ๆ เช่นที่อาจได้รับรหัสที่จะกลายพันธุ์ในขณะที่อยู่ในพจนานุกรม หากรหัสที่เก็บวัตถุปฏิบัติตามกฎนั้นการมีรหัสแฮชซึ่งสะท้อนถึงสถานะที่ไม่แน่นอนอาจมีประโยชน์ มันแย่มาก. NET ไม่ได้แยกแยะความเท่าเทียมกันของรัฐและความเท่าเทียมกันได้ดีกว่าเพราะทั้งคู่เป็นแนวคิดที่มีประโยชน์
supercat

3
@Acentric: ยิ่งไปกว่าการใช้รหัสแฮชสำหรับการจัดการกับตารางแฮชแนวคิดพื้นฐานที่อยู่เบื้องหลังรหัสแฮชคือความรู้ที่ว่าวัตถุสองชิ้นมีรหัสแฮชที่แตกต่างกันแสดงว่าพวกเขาไม่เท่ากันและไม่ต้องการเปรียบเทียบ ในฐานะที่เป็นข้อพิสูจน์ความรู้ที่ว่ารหัสแฮชของวัตถุจำนวนมากไม่ตรงกับรหัสแฮชของวัตถุที่ระบุหมายความว่าไม่มีสิ่งใดเทียบเท่ากับวัตถุนั้น การใช้รหัสแฮชสำหรับการกำหนดที่อยู่นั้นเป็นวิธีการละเว้นวัตถุที่มีรหัสแฮชที่แตกต่างกัน
supercat

9

GetHashCodeใช้ในการรวบรวมพจนานุกรมและสร้างแฮชสำหรับจัดเก็บวัตถุในนั้น นี่เป็นบทความที่ดีว่าทำไมและวิธีการใช้IEqualtyComparerและGetHashCode http://dotnetperls.com/iequalitycomparer


4
เพิ่มเติม: ถ้าคุณต้องการที่จะเปรียบเทียบเท่ากับจะ enouf แต่เมื่อคุณจำเป็นต้องได้รับองค์ประกอบจากพจนานุกรมมันเป็นเรื่องง่ายที่จะทำเช่นนี้โดยกัญชาไม่ได้โดยใช้เท่ากับ
Ash

5

ในขณะที่มันเป็นไปได้ที่ a Dictionary<TKey,TValue>จะมีGetValueวิธีการที่คล้ายกันเรียกร้องให้Equalsทุกคีย์ที่เก็บไว้เพื่อดูว่ามันตรงกับที่ต้องการหรือไม่นั้นจะช้ามาก แต่เช่นเดียวกับคอลเลกชันที่ใช้แฮชหลายครั้งมันต้องอาศัยGetHashCodeการยกเว้นค่าที่ไม่ตรงกันส่วนใหญ่ออกจากการพิจารณาอย่างรวดเร็ว หากการโทรGetHashCodeหารายการที่ต้องการ 42 และคอลเลกชันมี 53,917 รายการ แต่การเรียกGetHashCodeรายการ 53,914 รายการมีค่าอื่นที่ไม่ใช่ 42 จะมีเพียง 3 รายการเท่านั้นที่จะถูกเปรียบเทียบกับรายการที่ต้องการ อีก 53,914 คนอาจถูกเพิกเฉยได้อย่างปลอดภัย

เหตุผลที่GetHashCodeรวมอยู่ในIEqualityComparer<T>คือเพื่อให้เป็นไปได้ที่ผู้บริโภคของพจนานุกรมอาจต้องการพิจารณาว่าเป็นวัตถุที่เท่ากันซึ่งปกติแล้วจะไม่ถือว่ากันและกันเท่ากัน ตัวอย่างที่พบบ่อยที่สุดคือผู้โทรที่ต้องการใช้สตริงเป็นคีย์ แต่ใช้การเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ เพื่อให้การทำงานมีประสิทธิภาพพจนานุกรมจะต้องมีฟังก์ชันแฮชบางรูปแบบที่จะให้ค่าเดียวกันสำหรับ "Fox" และ "FOX" แต่หวังว่าจะให้ผลอย่างอื่นสำหรับ "box" หรือ "zebra" เนื่องจากGetHashCodeวิธีการที่ใช้Stringไม่ได้ผลพจนานุกรมจะต้องได้รับวิธีการดังกล่าวจากที่อื่นIEqualityComparer<T>Equals วิธีการที่พิจารณาว่า "Fox" และ "FOX" เหมือนกัน แต่ไม่ใช่ "box" หรือ "zebra"


ถูกต้องและตรงประเด็นตอบคำถาม! GetHashCode () ต้องเสริม Equals () สำหรับวัตถุที่เป็นปัญหา
Sumith

@ ซูมิ ธ : มีการพูดคุยกันหลายครั้งเกี่ยวกับการบีบแตรพูดคุยเกี่ยวกับถัง แต่ฉันคิดว่ามันมีประโยชน์มากกว่าที่จะคิดว่าการแยกตัวออก หากการเปรียบเทียบมีราคาแพง hashing อาจให้ประโยชน์แม้ในขณะที่ใช้คอลเลกชันที่ไม่ได้จัดระเบียบไว้ในที่เก็บข้อมูล
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.