1. 程式人生 > >.Net中自定義類作為Dictionary的key詳解

.Net中自定義類作為Dictionary的key詳解

在定義資料結構時,Dictionary提供了快速查詢資料的功能,另外Dictionary< TKey, TValue >屬於key-value鍵值對資料結構,提供了泛型的靈活性,是資料結構的一個利器,但是目前擁有的string,int,bool等基礎資料型別並不能滿足我們的需求,那麼如何把自定義的資料類作為Dictionary的key呢?

首先需要搞清楚以下問題

  • Dictionary是如何通過key找到的value?
  • 需要怎麼做才可以讓Dictionary認同我們的自定義類作為Key?
  • 具體程式碼應該怎麼寫,去實現?

一、Dictionary是如何通過key找到的value?

首先提幾個簡單的概念,對我們理解Dictionary有很大的幫助。

雜湊表

定義:是一種用於描述有聯絡鍵值對資料結構的表。
.NET中有Hashtable的型別,它是一個通過關鍵字直接訪問記憶體儲存位置的資料結構。Dict使用了它,也就是說,Dictionary內部的key,value的存放與查詢其實都是由固定的記憶體地址的,但是資料量多了難免會出現多個key或者value擁有同樣的記憶體地址,這樣我們再通過定址操作資料時就會出現衝突的問題,這個我們叫碰撞衝突。

Dict解決碰撞衝突的方案

Dict採用分離連結法,什麼意思呢?就是當不同value被分配到同一個地址時,Dict會在那個地址下再建一個連結,用於存放不同value,放張圖大家應該就知道是怎麼回事了。
這裡寫圖片描述

明白了以上兩個概念,我們基本上就可以理解這句話了:
Dict判斷查詢key時,首先會呼叫GetHashCode方法,來取得key的Hashtable,判斷Hashtable是否一致,如果Hashtable一樣還不算找到,還需要繼續判斷存放的value是不是(相等)Equal,兩個條件都滿足,才算真正找到了我們需要的key,然後取出Dictionary存放的value值。

二、需要怎麼做才可以讓Dictionary認同我們的自定義類作為Key?

舉一個栗子:

using System.Collections.Generic;
using System.Linq;
using System.Text;
using
System.Threading.Tasks; namespace test_Dict { class Program { class MyClass { private int ID { get; set; } private string name { get; set; } public MyClass(int ID, string name) { this.ID = ID; this.name = name; } } static void Main(string[] args) { MyClass class_one = new MyClass(100,"nini_boom"); MyClass class_two = new MyClass(200,"liyang"); MyClass class_three = new MyClass(100, "nini_boom"); Dictionary<MyClass, int> _TestDict = new Dictionary<MyClass, int>(); _TestDict.Add(class_one,1); _TestDict.Add(class_two,2); Console.WriteLine(_TestDict.ContainsKey(class_three)); Console.ReadKey(); } } }

可以看出,即使我們的class_three和class_one在內容上完全相等,但是Dict中仍然找不到。為什麼呢?

因為Dict認為的key相等和我們認為的相等不一樣
為什麼Dict不認為相等呢?我們知道,.Net中所有的資料結構都是繼承了object,但是object本身對繼承了它的資料一無所知,所以為了防止繼承了objece的資料發生碰撞衝突現象,所以object的做法是讓每一個繼承它的資料的Hashtable都儘量不一樣,因為我們的MyClass也是繼承了object的,所以Dict在比較Class_one與class_three 的Hashtable時就認為他倆不相等,所以找不到。

為了實現在內容上相等的類,就可以尋找到同樣內容的Key,也就是讓自定義類作為Dictionary的key,我們需要重新定義一套比較的規定,來滿足我們認為的相等

三、具體程式碼怎麼寫,去實現?

在Dict的內部實現中,比較兩個key是否相等用到了兩個方法:GetHashCode()和Equal(),所以接下來需要在自定義類中重寫GetHashCode()和Equal()方法,分別得出Hashtable和比較規則,下次Dict再次比較時就會呼叫重寫的GetHashCode和Equal來進行比較。
再拿上面的栗子舉下,增加兩個方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace test_Dict
{
    class Program
    {

        class MyClass
        {
            private int ID { get; set; }
            private string name { get; set; }
            public MyClass(int ID, string name)
            {
                this.ID = ID;
                this.name = name;
            }

            public override bool Equals(object obj)
            {
                var your_class = (MyClass)obj;
                return your_class.ID == this.ID && your_class.name == this.name;
            }

            public override int GetHashCode()
            {
                int id_hashcode = ID.GetHashCode();
                int name_hashcode = name.GetHashCode();
                return id_hashcode + name_hashcode;
            }

        } 

        static void Main(string[] args)
        {
            MyClass class_one = new MyClass(100,"nini_boom");
            MyClass class_two = new MyClass(200,"liyang");

            MyClass class_three = new MyClass(100, "nini_boom");

            Dictionary<MyClass, int> _TestDict = new Dictionary<MyClass, int>();
            _TestDict.Add(class_one,1);
            _TestDict.Add(class_two,2);

            Console.WriteLine(_TestDict.ContainsKey(class_three));
            Console.ReadKey();
        }
    }
}

這次返回的就是True了。