[C#] 閱讀Hashset的一些感想
Equals和GetHashCode
在瞭解Hashset之前,先看看程式碼實現。在Hashset中,要用到兩個函式:Equals和GetHashCode。因為Equals的實現,同時也要重寫GetHashCode方法。
那麼,為什麼要重寫GetHashCode函式呢?且看看官方警告:如果重寫 GetHashCode 方法,您應重寫 Equals, ,反之亦然。如果被重寫 Equals 方法將返回 true 兩個物件是否相等,被重寫的測試時GetHashCode 方法必須返回兩個物件相同的值。這裡很明確說到,GetHashCode就是為了能讓Equals相同的兩個物件,有相同的Hash值。
Hashset儲存原理
Hashset類中有多個成員變數,在儲存中需要我們值得注意的就是 int[] buckets、Slot[] slots、int freeIndex、int lastIndex等,分別標記了對應雜湊值的index、Hashset物件集合、空的位置和集合最後一個位置。其中,Slot是個結構體,包涵3個成員:int hashcode、T value、int next,分別是雜湊值、物件和下一個(相同Hash值)物件的位置。
當我們查詢一個物件,是否在Hashset中時,會利用該物件的GetHashCode,通過計算得到對應的bucket值(bucket儲存插入操作最後一個物件在slot中的位置,稍後描述),然後再利用Equals來比較物件是否相等,不等則一直比較至Next=0為止。
所以說,Hashset是基於陣列和連結串列管理的。
插入和刪除
插入的時候,先查詢集合中是否有相同變數(見儲存原理),若沒有,則開始插入新成員。首先看freeIndex是否有值(該值僅在刪除操作後產生),若有就用這個位置,若沒有,則用lastIndex(最後一個位置,用完後+1)。因而,找到了插入位置後,開始計算該slot的值:hashcode由GetHashCode產生,value就是新成員,而Next則是用HashCode計算對應位置中的buckets值(儲存相同Hashcode值的物件在slots中的位置),並重新整理bucket值為當前slot。因而我們知道,由於Next的計算機制,新插入的物件,會覆蓋原來buckets的值。
刪除操作,同插入一樣,也要查詢到物件。如果是bucket中第一個物件,在刪除物件同時,bucket修改為該物件Next值,否則,則修改上一個物件的Next為該物件的Next(相當於將上個物件指標直接指向下一個物件)。然後,刪除的slot值需要修正:hashcode=-1,value=default,next=freeList。所以我們知道,空位置也形成了一個連結串列。
所以,可以看出HashCode管理起來,也需要一點時間的。但是,由於插入和刪除都用了陣列+連結串列,故速度還是很快的。
遍歷
遍歷使用foreach。這裡要說一點,遍歷的順序和插入的順序可能會不相同(如,對插入第一個資料刪除,再插入,則會得到該現象)。Hashset實現了IEnumerable介面,並通過version變數,確保資料在遍歷中不發生變化(操作會改變version值)。
相信大家都很瞭解IEnumerable介面了吧。還有個泛型介面IEnumerable<T>。這裡說下Current,在兩個介面中都要實現,而非泛型介面Current是要用到的,需要完成“丟擲異常”功能(索引超出則丟擲異常),並呼叫了泛型介面Current實現。
好,迴歸遍歷:MoveNext 就是對slot進行順序遍歷了,如果hashcode>=0,則說明輸出有效。
總結
總的來說,Hashset通過GetHashCode值(hash相等才會再比較equal)和Equal方法確定是否有相同變數,沒有則可以插入。儲存是通過陣列和連結串列結合而完成的。
如果學過了演算法與資料結構應該很明白其原理了。當初我記得還有什麼二次雜湊演算法來者的,反正好久沒再接觸了。
本人小白,本文章僅作學習總結,也作交流使用。。
如有錯誤,歡迎指點,謝謝!
插入操作的原始碼
private bool AddIfNotPresent(T value)
{
int freeList;
if (this.m_buckets == null)
{
this.Initialize(0);
}
int hashCode = this.InternalGetHashCode(value);
int index = hashCode % this.m_buckets.Length;
int num3 = 0;
for (int i = this.m_buckets[hashCode % this.m_buckets.Length] - 1; i >= 0; i = this.m_slots[i].next)
{
if ((this.m_slots[i].hashCode == hashCode) && this.m_comparer.Equals(this.m_slots[i].value, value))
{
return false;
}
num3++;
}
if (this.m_freeList >= 0)
{
freeList = this.m_freeList;
this.m_freeList = this.m_slots[freeList].next;
}
else
{
if (this.m_lastIndex == this.m_slots.Length)
{
this.IncreaseCapacity();
index = hashCode % this.m_buckets.Length;
}
freeList = this.m_lastIndex;
this.m_lastIndex++;
}
this.m_slots[freeList].hashCode = hashCode;
this.m_slots[freeList].value = value;
this.m_slots[freeList].next = this.m_buckets[index] - 1;
this.m_buckets[index] = freeList + 1;
this.m_count++;
this.m_version++;
if ((num3 > 100) && HashHelpers.IsWellKnownEqualityComparer(this.m_comparer))
{
this.m_comparer = (IEqualityComparer<T>) HashHelpers.GetRandomizedEqualityComparer(this.m_comparer);
this.SetCapacity(this.m_buckets.Length, true);
}
return true;
}
相關推薦
[C#] 閱讀Hashset的一些感想
Equals和GetHashCode 在瞭解Hashset之前,先看看程式碼實現。在Hashset中,要用到兩個函式:Equals和GetHashCode。因為Equals的實現,同時也要重寫GetHashCode方法。 那麼,為什麼要重寫GetHashCode函式呢?且
關於C語言學習的一些感想(初學者)
C語言是一門通用計算機程式語言,廣泛應用於底層開發。C語言的設計目標是提供一種能以簡易的方式編譯、處理低階儲存器、產生少量的機器碼以及不需要任何執行環境支援便能執行的程式語言。 儘管C語言提供了許多低階處理的功能,但仍然保持著良好跨平臺的特性,以一個
C++ 中的一些錯覺
free color users main light cli .com led tor 1. 默認構造函數和不帶參數的構造函數之間無聯系 默認構造函數是編譯器發現類不存在顯式構造函數時自動生成的無參數的構造函數。同樣,用戶可以定義顯示的無參數構造函數。 2. 在構造函數、
高德地圖和canvas畫圖結合應用的一些感想(一)
記錄 src function lose poi pan 高德 工程師 根據地 之前朋友委托有個創業項目,想讓我幫忙,正好那段時間有點閑,半推半就中就答應下來了。 入了團隊才發現,該項目前後端分離,後端工程師已就位主要實現接口,IOS端工程師也已就位,還差一個web
C# Cache的一些總結
scl div key true 必須 ada reader 執行過程 摘要 最近我們的系統面臨著嚴峻性能瓶頸問題,這是由於訪問量增加,客戶端在同一時間請求增加,這迫使我們要從兩個方面解決這一問題,增加硬件和提高系統的性能。 大家可以通過各種各樣的方法去優化我們系統,本篇博
iOS -- Effective Objective-C 閱讀筆記 (7)
成對 format 開發 數據 清晰 rip 相同 命名法 定義 1: 實現 description 方法 NSlog 在輸出自定義的類時, 只輸出了 類名 和 對象的內存地址. 要想輸出更為有用的信息也很簡單, 只需要覆寫 description 方法並將描述此對象的字符
C語言的一些輸出格式
print 科學 數據 相同 無符號整數 col 16進制 大於 mic %e printf()的一種輸出格式 科學表示的一種浮點數 1.24==1.240000e+000 1240000==1.240000e+006
記錄開發Nodejs c++ addon的一些經驗(一、技術棧)
c++編寫 fstream href ren http lan www. 記錄 ref Nodejs c++ addon 是用c++去編寫Nodejs的插件 技術棧: 1、node-gyp 一個用於把c++文件編譯成node可執行文件的庫 2、v8 google
iOS -- Effective Objective-C 閱讀筆記 (8)
cti 結構 事件處理 決定 smu 擁有 ive 總結 dst 若想令自己縮寫的對象具有拷貝功能, 則需要實現 NSCopying 協議, 如果自定義的對象分為可變版本與不可變版本, 那麽就要同時實現 NSCopying 協議和 NSMutableCopying 協議 復
C學習筆記-一些知識
如果 結構 一個 pre test c學習 int 學習 ise memset可以方便的清空一個結構類型的變量或數組。如: struct sample_struct { char csName[16]; int iSeq; int iT
讀研以來的一些感想:名校好在哪裏?
工作 研究生 同學 服務 我想 學院 忘記 老師 第一時間 讀研半年以來,逐漸了解了學校對學生的培養是怎麽樣的,同時我把現在的學校做的一些舉措跟我本科的學校做了下對比,頓時感慨良多:名校與普通學校的對學生的培養真的非常不一樣。我本科是普通一本,研究生讀的是一所很不錯的985
floyed算法的一些感想
i++ 而不是 動態規劃 記錄 感覺 none 距離 splay 為什麽 雖然我還在學動態規劃,但這並不影響我去試圖研究floyed,在對floyed算法進行研究了40min後,我感覺我似乎應該好像是勉強理解了floyed算法 for(int k=1;k<=n;k++
關於Python的一些感想
課程 不能 學習 世界 時代 OS clas 為知 事情 最初選擇Python是因為知道這是目前世界上最熱門的語言,作為一個學電子商務的學生當然是不能脫離計算機的大潮的!但上學期的C語言的學習讓我覺得程序設計真的不是一件容易的事情,但在這短短的幾節課裏,我深深感受到了這門語
java源碼閱讀HashSet
ray valid get bool return 不變 implement checked nts 1類簽名與註解 public class HashSet<E> extends AbstractSet<E> implem
C#中的一些基礎
nal 一個 out C# pro char 結構 over 基礎 值類型與引用類型 值類型包括:【基本數據類型,如int,double,char,bool等】【枚舉類型enum】【結構類型struct】 引用類型包括:【類類型,如基類Object,字符串類String等】
學習C++時的一些小知識點
標識 程序 c程序 num 定義 來講 是否 一個 別名 (轉載https://www.cnblogs.com/qyaizs/articles/2039101.html) struct和typedef struct 分三塊來講述: 1 首先://註意在C和C++裏不同
隨筆 | 對軟件工程的一些感想
對口 重要 提高 職位 老師 做出 準備 區別 局域網 隨筆的想法來自博客https://www.cnblogs.com/greyzeng/p/9581624.html 第一部分:結緣計算機 你為什麽選擇計算機專業?你認為你的條件如何?和這些博主比呢? 計算機是你喜歡的領
學習python的一些感想
幫助 第一次 blog div 語法 c語言 班會 數據 log 我在大一入學的第一次班會中就得知了我們專業的學習需要用到python,利用網絡爬蟲從網上獲取信息。在大一的時候學習過c語言,對於編程語言有著一點認識,希望我可以在未來運用學到的python知識為我的專業學習
給大一新生學習c程序的一些建議的一些建議
使用 視頻 計算 群聊 文章 方法 大三 經驗 答案 這是一篇給剛學習c程序的學弟們的一篇日誌。如果想學好c程序,以及不想走太多彎路,希望能看一下這篇文章,如果說基礎較好,或者說已經是大二,大三,這篇文章不會有什麽幫助。 剛轉到軟件工程系,加了幾個
CLR VIA C# 閱讀筆記和感悟(三)
前言:如今.NetCore已經進入了2.1版本,但這本書的學習還是很重要,我們繼續學習和總結,以便於在.NetCore新技術的學習上能有個對比,幫助我們學習新知識。 執行緒池:執行緒的建立和銷燬都是要消耗資源的,所以微軟為了優化執行緒的使用,提出了執行緒池,執行緒池中的執行緒是可重用的,線上程初始