Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 久久国产精品自线拍免费,日韩不卡视频在线,欧美在线高清

          整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          一個合格的程序員必會Hash詳解

          • . 哈希表的基本思想
          • 2. 哈希表的相關(guān)基本概念
          • 3. 哈希表的實現(xiàn)方法
          • 4. 哈希表“定址”的方法
          • 5. 哈希表“解決沖突”的方法
          • 6. 哈希表“定址”和“解決沖突”之間的權(quán)衡
          • 7. 哈希表實例

          哈希表(Hash Table)是一種特殊的數(shù)據(jù)結(jié)構(gòu),它最大的特點就是可以快速實現(xiàn)查找、插入和刪除。因為它獨有的特點,Hash表經(jīng)常被用來解決大數(shù)據(jù)問題,也因此被廣大的程序員所青睞。為了能夠更加靈活地使用Hash來提高我們的代碼效率,今天,我們就談一談Hash的那點事。

          1. 哈希表的基本思想

          我們知道,數(shù)組的最大特點就是:尋址容易,插入和刪除困難;而鏈表正好相反,尋址困難,而插入和刪除操作容易。那么如果能夠結(jié)合兩者的優(yōu)點,做出一種尋址、插入和刪除操作同樣快速容易的數(shù)據(jù)結(jié)構(gòu),那該有多好。這就是哈希表創(chuàng)建的基本思想,而實際上哈希表也實現(xiàn)了這樣的一個“夙愿”,哈希表就是這樣一個集查找、插入和刪除操作于一身的數(shù)據(jù)結(jié)構(gòu)。

          回到頂部

          2. 哈希表的相關(guān)基本概念

          在介紹Hash之前,首先我們要搞明白幾個概念:

          哈希表(Hash Table):也叫散列表,是根據(jù)關(guān)鍵碼值(Key-Value)而直接進(jìn)行訪問的數(shù)據(jù)結(jié)構(gòu),也就是我們常用到的map。

          哈希函數(shù):也稱為是散列函數(shù),是Hash表的映射函數(shù),它可以把任意長度的輸入變換成固定長度的輸出,該輸出就是哈希值。哈希函數(shù)能使對一個數(shù)據(jù)序列的訪問過程變得更加迅速有效,通過哈希函數(shù)數(shù)據(jù)元素能夠被很快的進(jìn)行定位。

          哈希表和哈希函數(shù)的標(biāo)準(zhǔn)定義:

          • 若關(guān)鍵字為k,則其值存放在f(k)的存儲位置上。由此,不需比較便可直接取得所查記錄。稱這個對應(yīng)關(guān)系f為哈希函數(shù),按這個思想建立的表為哈希表。

          設(shè)所有可能出現(xiàn)的關(guān)鍵字集合記為U(簡稱全集)。實際發(fā)生(即實際存儲)的關(guān)鍵字集合記為K(|K|比|U|小得多)。

          散列方法是使用函數(shù)h將U映射到表T[0..m-1]的下標(biāo)上(m=O(|U|))。這樣以U中關(guān)鍵字為自變量,以h為函數(shù)的運算結(jié)果就是相應(yīng)結(jié)點的存儲地址。從而達(dá)到在O(1)時間內(nèi)就可完成查找。

          其中:

          ① h:U→{0,1,2,…,m-1} ,通常稱h為哈希函數(shù)(Hash Function)。哈希函數(shù)h的作用是壓縮待處理的下標(biāo)范圍,使待處理的|U|個值減少到m個值,從而降低空間開銷。

          ② T為哈希表(Hash Table)。

          ③ h(Ki)(Ki∈U)是關(guān)鍵字為Ki結(jié)點存儲地址(亦稱散列值或散列地址)。

          ④ 將結(jié)點按其關(guān)鍵字的哈希地址存儲到哈希表中的過程稱為散列(Hashing)

          1)沖突:

          兩個不同的關(guān)鍵字,由于散列函數(shù)值相同,因而被映射到同一表位置上。該現(xiàn)象稱為沖突(Collision)或碰撞。發(fā)生沖突的兩個關(guān)鍵字稱為該散列函數(shù)的同義詞(Synonym)。

          2)安全避免沖突的條件:

          最理想的解決沖突的方法是安全避免沖突。要做到這一點必須滿足兩個條件:

          ①其一是|U|≤m

          ②其二是選擇合適的散列函數(shù)。

          這只適用于|U|較小,且關(guān)鍵字均事先已知的情況,此時經(jīng)過精心設(shè)計散列函數(shù)h有可能完全避免沖突。

          3)沖突不可能完全避免

          通常情況下,h是一個壓縮映像。雖然|K|≤m,但|U|>m,故無論怎樣設(shè)計h,也不可能完全避免沖突。因此,只能在設(shè)計h時盡可能使沖突最少。同時還需要確定解決沖突的方法,使發(fā)生沖突的同義詞能夠存儲到表中。

          4)影響沖突的因素

          沖突的頻繁程度除了與h相關(guān)外,還與表的填滿程度相關(guān)。

          設(shè)m和n分別表示表長和表中填入的結(jié)點數(shù),則將α=n/m定義為散列表的裝填因子(Load Factor)。α越大,表越滿,沖突的機會也越大。通常取α≤1。

          回到頂部

          3. 哈希表的實現(xiàn)方法

          我們之前說了,哈希表是一個集查找、插入和刪除操作于一身的數(shù)據(jù)結(jié)構(gòu)。那這么完美的數(shù)據(jù)結(jié)構(gòu)到底是怎么實現(xiàn)的呢?哈希表有很多種不同的實現(xiàn)方法,為了實現(xiàn)哈希表的創(chuàng)建,這些所有的方法都離不開兩個問題——“定址”和“解決沖突”。

          在這里,我們通過詳細(xì)地介紹哈希表最常用的方法——取余法(定值)+拉鏈法(解決沖突),來一起窺探一下哈希表強大的優(yōu)點。

          取余法大家一定不會感覺陌生,就是我們經(jīng)常說的取余數(shù)的操作。

          拉鏈法是什么,“拉鏈”說白了就是“鏈表數(shù)組”。我這么一解釋,大家更暈了,啥是“鏈表數(shù)組”?。繛榱烁玫亟忉尅版湵頂?shù)組”,我們用下圖進(jìn)行解釋:圖中的主干部分是一個順序存儲結(jié)構(gòu)數(shù)組,但是有的數(shù)組元素為空,有的對應(yīng)一個值,有的對應(yīng)的是一個鏈表,這就是“鏈表數(shù)組”。比如數(shù)組0的位置對應(yīng)一個鏈表,鏈表有兩個元素“496”和“896”,這說明元素“496”和“896”有著同樣的Hash地址,這就是我們上邊介紹的“沖突”或者“碰撞”。但是“鏈表數(shù)組”的存儲方式很好地解決了Hash表中的沖突問題,發(fā)生沖突的元素會被存在一個對應(yīng)Hash地址指向的鏈表中。實際上,“鏈表數(shù)組”就是一個指針數(shù)組,每一個指針指向一個鏈表的頭結(jié)點,鏈表可能為空,也可能不為空。

          說完這些,大家肯定已經(jīng)理解了“鏈表數(shù)組”的概念,那我們就一起看看Hash表是如何根據(jù)“取余法+拉鏈法”構(gòu)建的吧。

          將所有關(guān)鍵字為同義詞的結(jié)點鏈接在同一個鏈表中。若選定的散列表長度為m,則可將散列表定義為一個由m個頭指針組成的指針數(shù)組T[0..m-1]。凡是散列地址為i的結(jié)點,均插入到以T[i]為頭指針的單鏈表中。T中各分量的初值均應(yīng)為空指針。在拉鏈法中,裝填因子α可以大于1,但一般均取α≤1。

          舉例說明拉鏈法的執(zhí)行過程,設(shè)有一組關(guān)鍵字為(26,36,41,38,44,15,68,12,6,51),用取余法構(gòu)造散列函數(shù),初始情況如下圖所示:

          最終結(jié)果如下圖所示:

          理解了Hash表的創(chuàng)建,那根據(jù)建立的Hash表進(jìn)行查找就很容易被理解了。

          查找操作,如果理解了插入和刪除,查找操作就比較簡單了,令待查找的關(guān)鍵字是x,也可分為幾種情況:

          1)x所屬的Hash地址未被占用,即不存在與x相同的Hash地址關(guān)鍵字,當(dāng)然也不存在x了;

          2)x所屬的Hash地址被占用了,但它所存的關(guān)鍵不屬于這個Hash地址,與1)相同,不存在與x相同Hash地址的關(guān)鍵字;

          3)x所屬的Hash地址被占用了,且它所存的關(guān)鍵屬于這個Hash地址,即存在與x相同sHash地址的關(guān)鍵字,只是不知這個關(guān)鍵字是不是x,需要進(jìn)一步查找。

          由此可見,Hash表插入、刪除和插入操作的效率都相當(dāng)?shù)母摺?/p>

          思考一個問題:如果關(guān)鍵字是字符串怎么辦?我們怎么根據(jù)字符串建立Hash表?

          通常都是將元素的key轉(zhuǎn)換為數(shù)字進(jìn)行散列,如果key本身就是整數(shù),那么散列函數(shù)可以采用keymod tablesize(要保證tablesize是質(zhì)數(shù))。而在實際工作中經(jīng)常用字符串作為關(guān)鍵字,例如身姓名、職位等等。這個時候需要設(shè)計一個好的散列函數(shù)進(jìn)程處理關(guān)鍵字為字符串的元素。參考《數(shù)據(jù)結(jié)構(gòu)與算法分析》第5章,有以下幾種處理方法:

          方法1:將字符串的所有的字符的ASCII碼值進(jìn)行相加,將所得和作為元素的關(guān)鍵字。設(shè)計的散列函數(shù)如下所示:

          int hash(const string& key,int tablesize)
          {
           int hashVal = 0;
           for(int i=0;i<key.length();i++)
           hashVal += key[i];
           return hashVal % tableSize;
          }
          

          此方法的缺點是不能有效的分布元素,例如假設(shè)關(guān)鍵字是有8個字母構(gòu)成的字符串,散列表的長度為10007。字母最大的ASCII碼為127,按照方法1可得到關(guān)鍵字對應(yīng)的最大數(shù)值為127×8=1016,也就是說通過散列函數(shù)映射時只能映射到散列表的槽0-1016之間,這樣導(dǎo)致大部分槽沒有用到,分布不均勻,從而效率低下。

          方法2:假設(shè)關(guān)鍵字至少有三個字母構(gòu)成,散列函數(shù)只是取前三個字母進(jìn)行散列。設(shè)計的散列函數(shù)如下所示:

          int hash(const string& key,int tablesize)
          {
           //27 represents the number of letters plus the blank
           return (key[0]+27*key[1]+729*key[2])%tablesize;
          }
          

          該方法只是取字符串的前三個字符的ASCII碼進(jìn)行散列,最大的得到的數(shù)值是2851,如果散列的長度為10007,那么只有28%的空間被用到,大部分空間沒有用到。因此如果散列表太大,就不太適用。

          方法3:借助Horner's 規(guī)則,構(gòu)造一個質(zhì)數(shù)(通常是37)的多項式,(非常的巧妙,不知道為何是37)。計算公式為:key[keysize-i-1]*37i, 0<=i<keysize求和。設(shè)計的散列函數(shù)如下所示:

          int hash(const string & key,int tablesize)
          {
           int hashVal = 0;
           for(int i =0;i<key.length();i++)
           hashVal = 37*hashVal + key[i];
           hashVal %= tableSize;
           if(hashVal<0) //計算的hashVal溢出
           hashVal += tableSize;
           return hashVal;
          }
          

          該方法存在的問題是如果字符串關(guān)鍵字比較長,散列函數(shù)的計算過程就變長,有可能導(dǎo)致計算的hashVal溢出。針對這種情況可以采取字符串的部分字符進(jìn)行計算,例如計算偶數(shù)或者奇數(shù)位的字符。

          回到頂部

          4. 哈希表“定址”的方法

          其實常用的“定址”的手法有“五種”:

          1)直接定址法

          很容易理解,key=Value+C;這個“C"是常量。Value+C其實就是一個簡單的哈希函數(shù)。

          2)除法取余法

          key=value%C

          3)數(shù)字分析法

          這種蠻有意思,比如有一組value1=112233,value2=112633,value3=119033,針對這樣的數(shù)我們分析數(shù)中間兩個數(shù)比較波動,其他數(shù)不變。那么我們?nèi)ey的值就可以是key1=22,key2=26,key3=90。

          4)平方取中法

          5)折疊法

          舉個例子,比如value=135790,要求key是2位數(shù)的散列值。那么我們將value變?yōu)?3+57+90=160,然后去掉高位“1”,此時key=60,哈哈,這就是他們的哈希關(guān)系,這樣做的目的就是key與每一位value都相關(guān),來做到“散列地址”盡可能分散的目地。

          影響哈希查找效率的一個重要因素是哈希函數(shù)本身。當(dāng)兩個不同的數(shù)據(jù)元素的哈希值相同時,就會發(fā)生沖突。為減少發(fā)生沖突的可能性,哈希函數(shù)應(yīng)該將數(shù)據(jù)盡可能分散地映射到哈希表的每一個表項中。

          回到頂部

          5. 哈希表“解決沖突”的方法

          Hash表解決沖突的方法主要有以下兩種:

          1)開放地址法

          如果兩個數(shù)據(jù)元素的哈希值相同,則在哈希表中為后插入的數(shù)據(jù)元素另外選擇一個表項。當(dāng)程序查找哈希表時,如果沒有在第一個對應(yīng)的哈希表項中找到符合查找要求的數(shù)據(jù)元素,程序就會繼續(xù)往后查找,直到找到一個符合查找要求的數(shù)據(jù)元素,或者遇到一個空的表項。

          開放地址法包括線性探測、二次探測以及雙重散列等方法。其中線性探測法示意圖如下:

          散列過程如下圖所示:

          2)鏈地址法

          將哈希值相同的數(shù)據(jù)元素存放在一個鏈表中,在查找哈希表的過程中,當(dāng)查找到這個鏈表時,必須采用線性查找方法。

          回到頂部

          6. 哈希表“定址”和“解決沖突”之間的權(quán)衡

          雖然哈希表是在關(guān)鍵字和存儲位置之間建立了對應(yīng)關(guān)系,但是由于沖突的發(fā)生,哈希表的查找仍然是一個和關(guān)鍵字比較的過程,不過哈希表平均查找長度比順序查找要小得多,比二分查找也小。

          查找過程中需和給定值進(jìn)行比較的關(guān)鍵字個數(shù)取決于下列三個因素:哈希函數(shù)、處理沖突的方法和哈希表的裝填因子。

          哈希函數(shù)的"好壞"首先影響出現(xiàn)沖突的頻繁程度,但如果哈希函數(shù)是均勻的,則一般不考慮它對平均查找長度的影響。

          對同一組關(guān)鍵字,設(shè)定相同的哈希函數(shù),但使用不同的沖突處理方法,會得到不同的哈希表,它們的平均查找長度也不同。

          一般情況下,處理沖突方法相同的哈希表,其平均查找長度依賴于哈希表的裝填因子α。顯然,α越小,產(chǎn)生沖突的機會就越大;但α過小,空間的浪費就過多。通過選擇一個合適的裝填因子α,可以將平均查找長度限定在一個范圍內(nèi)。

          總而言之,哈希表“定址”和“解決沖突”之間的權(quán)衡決定了哈希表的性能。

          回到頂部

          7. 哈希表實例

          一個哈希表實現(xiàn)的C++實例,在此設(shè)計的散列表針對的是關(guān)鍵字為字符串的元素,采用字符串散列函數(shù)方法3進(jìn)行設(shè)計散列函數(shù),采用鏈接方法處理碰撞,然后采用根據(jù)裝載因子(指定為1,同時將n個元素映射到一個鏈表上,即n==m時候)進(jìn)行再散列。采用C++,借助vector和list,設(shè)計的hash表框架如下:

          template <class T>
          class HashTable
          {
          public:
           HashTable(int size = 101);
           int insert(const T& x);
           int remove(const T& x);
           int contains(const T& x);
           void make_empty();
           void display()const;
          private:
           vector<list<T> > lists;
           int currentSize;//當(dāng)前散列表中元素的個數(shù)
           int hash(const string& key);
           int myhash(const T& x);
           void rehash();
          };
          

          實現(xiàn)的完整程序如下所示:

          #include <iostream>
          #include <vector>
          #include <list>
          #include <string>
          #include <cstdlib>
          #include <cmath>
          #include <algorithm>
          using namespace std;
          int nextPrime(const int n);
          template <class T>
          class HashTable
          {
          public:
           HashTable(int size = 101);
           int insert(const T& x);
           int remove(const T& x);
           int contains(const T& x);
           void make_empty();
           void display()const;
          private:
           vector<list<T> > lists;
           int currentSize;
           int hash(const string& key);
           int myhash(const T& x);
           void rehash();
          };
          template <class T>
          HashTable<T>::HashTable(int size)
          {
           lists = vector<list<T> >(size);
           currentSize = 0;
          }
          template <class T>
          int HashTable<T>::hash(const string& key)
          {
           int hashVal = 0;
           int tableSize = lists.size();
           for(int i=0;i<key.length();i++)
           hashVal = 37*hashVal+key[i];
           hashVal %= tableSize;
           if(hashVal < 0)
           hashVal += tableSize;
           return hashVal;
          }
          template <class T>
          int HashTable<T>:: myhash(const T& x)
          {
           string key = x.getName();
           return hash(key);
          }
          template <class T>
          int HashTable<T>::insert(const T& x)
          {
           list<T> &whichlist = lists[myhash(x)];
           if(find(whichlist.begin(),whichlist.end(),x) != whichlist.end())
           return 0;
           whichlist.push_back(x);
           currentSize = currentSize + 1;
           if(currentSize > lists.size())
           rehash();
           return 1;
          }
          template <class T>
          int HashTable<T>::remove(const T& x)
          {
           typename std::list<T>::iterator iter;
           list<T> &whichlist = lists[myhash(x)];
           iter = find(whichlist.begin(),whichlist.end(),x);
           if( iter != whichlist.end())
           {
           whichlist.erase(iter);
           currentSize--;
           return 1;
           }
           return 0;
          }
          template <class T>
          int HashTable<T>::contains(const T& x)
          {
           list<T> whichlist;
           typename std::list<T>::iterator iter;
           whichlist = lists[myhash(x)];
           iter = find(whichlist.begin(),whichlist.end(),x);
           if( iter != whichlist.end())
           return 1;
           return 0;
          }
          template <class T>
          void HashTable<T>::make_empty()
          {
           for(int i=0;i<lists.size();i++)
           lists[i].clear();
           currentSize = 0;
           return 0;
          }
          template <class T>
          void HashTable<T>::rehash()
          {
           vector<list<T> > oldLists = lists;
           lists.resize(nextPrime(2*lists.size()));
           for(int i=0;i<lists.size();i++)
           lists[i].clear();
           currentSize = 0;
           for(int i=0;i<oldLists.size();i++)
           {
           typename std::list<T>::iterator iter = oldLists[i].begin();
           while(iter != oldLists[i].end())
           insert(*iter++);
           }
          }
          template <class T>
          void HashTable<T>::display()const
          {
           for(int i=0;i<lists.size();i++)
           {
           cout<<i<<": ";
           typename std::list<T>::const_iterator iter = lists[i].begin();
           while(iter != lists[i].end())
           {
           cout<<*iter<<" ";
           ++iter;
           }
           cout<<endl;
           }
          }
          int nextPrime(const int n)
          {
           int ret,i;
           ret = n;
           while(1)
           {
           int flag = 1;
           for(i=2;i<sqrt(ret);i++)
           if(ret % i == 0)
           {
           flag = 0;
           break;
           }
           if(flag == 1)
           break;
           else
           {
           ret = ret +1;
           continue;
           }
           }
           return ret;
          }
          class Employee
          {
          public:
           Employee(){}
           Employee(const string n,int s=0):name(n),salary(s){ }
           const string & getName()const { return name; }
           bool operator == (const Employee &rhs) const
           {
           return getName() == rhs.getName();
           }
           bool operator != (const Employee &rhs) const
           {
           return !(*this == rhs);
           }
           friend ostream& operator <<(ostream& out,const Employee& e)
           {
           out<<"("<<e.name<<","<<e.salary<<") ";
           return out;
           }
          private:
           string name;
           int salary;
          };
          int main()
          {
           Employee e1("Tom",6000);
           Employee e2("Anker",7000);
           Employee e3("Jermey",8000);
           Employee e4("Lucy",7500);
           HashTable<Employee> emp_table(13);
           emp_table.insert(e1);
           emp_table.insert(e2);
           emp_table.insert(e3);
           emp_table.insert(e4);
           cout<<"Hash table is: "<<endl;
           emp_table.display();
           if(emp_table.contains(e4) == 1)
           cout<<"Tom is exist in hash table"<<endl;
           if(emp_table.remove(e1) == 1)
           cout<<"Removing Tom form the hash table successfully"<<endl;
           if(emp_table.contains(e1) == 1)
           cout<<"Tom is exist in hash table"<<endl;
           else
           cout<<"Tom is not exist in hash table"<<endl;
           //emp_table.display();
           exit(0);
          }
          

          作者:Poll的筆記

          原文:http://www.cnblogs.com/maybe2030/p/4719267.html

          文鏈接:https://www.infoworld.com/article/3586972/how-to-use-hashset-in-csharp.html

          HashSet 是一個優(yōu)化過的無序集合,提供對元素的高速查找和高性能的set集合操作,而且 HashSet 是在 .NET 3.5 中被引入的,在 System.Collection.Generic 命名空間下,這篇就來討論一下如何使用這個 HashSet。

          要運行本篇文章的案例代碼,你需要安裝一下 Visual Studio 2019,如果沒有的話可以到官網(wǎng)下載一下。

          使用 VS 創(chuàng)建一個 .NET Core 控制臺程序

          首先,我通過 VS2019 創(chuàng)建一個 .NET Core 控制臺程序,創(chuàng)建可以參考下面步驟:

          • 打開 Visual Studio IDE

          • 點擊創(chuàng)建 Create new project

          • Create new project 窗口上,從模板列表中選擇:Console App (.NET Core)

          • 點擊下一步

          • Configure your new project 界面填好你的項目名稱和存放路徑

          這樣我們就創(chuàng)建好了一個新項目,本文的后面部分就會在這個項目里來給大家分享 HashSet 的一些必備知識。

          HashSet 到底是什么

          所謂的HashSet,指的就是 System.Collections.Generic 命名空間下的 HashSet<T> 類,它是一個高性能,無序的集合,因此HashSet它并不能做排序操作,也不能包含任何重復(fù)的元素,Hashset 也不能像數(shù)組那樣使用索引,所以在 HashSet 上你無法使用 for 循環(huán),只能使用 foreach 進(jìn)行迭代,HashSet 通常用在處理元素的唯一性上有著超高的性能。

          HashSet<T> 實現(xiàn)了如下幾個接口:

          
          public class HashSet<T> : System.Collections.Generic.ICollection<T>,
          System.Collections.Generic.IEnumerable<T>, 
          System.Collections.Generic.IReadOnlyCollection<T>,
          System.Collections.Generic.ISet<T>,
          System.Runtime.Serialization.IDeserializationCallback,
          System.Runtime.Serialization.ISerializable
          {
          }
          

          HashSet 只能包含唯一的元素,它的內(nèi)部結(jié)構(gòu)也為此做了專門的優(yōu)化,值得注意的是,HashSet 也可以存放單個的 null 值,可以得出這么一個結(jié)論:如何你想擁有一個具有唯一值的集合,那么 HashSet 就是你最好的選擇,何況它還具有超高的檢索性能。

          從 HashSet 中查找一個元素

          如果想判斷某一個元素是否在 HashSet 內(nèi),建議使用 Contains 進(jìn)行判斷,代碼如下:

          
                  static void Main(string[] args)
                  {
                      HashSet<string> hashSet = new HashSet<string>();
                      hashSet.Add("A");
                      hashSet.Add("B");
                      hashSet.Add("C");
                      hashSet.Add("D");
                      if (hashSet.Contains("D"))
                          Console.WriteLine("The required element is available.");
                      else
                          Console.WriteLine("The required element isn’t available.");
                      Console.ReadKey();
                  }
          

          HashSet中的元素唯一性

          如果你向 HashSet 中插入重復(fù)的元素,它的內(nèi)部會忽視這次操作而不像別的集合一樣拋出異常,接下來展示一下代碼:

          
                  static void Main(string[] args)
                  {
                      HashSet<string> hashSet = new HashSet<string>();
                      hashSet.Add("A");
                      hashSet.Add("B");
                      hashSet.Add("C");
                      hashSet.Add("D");
                      hashSet.Add("D");
                      Console.WriteLine("The number of elements is: {0}", hashSet.Count);
                      Console.ReadKey();
                  }
          

          當(dāng)你執(zhí)行了這個程序,輸出結(jié)果如下圖:

          現(xiàn)在可以考慮一下下面的代碼段,它展示了重復(fù)的元素是如何被剔除的。

          
                  static void Main(string[] args)
                  {
                      string[] cities = new string[] {
                          "Delhi",
                          "Kolkata",
                          "New York",
                          "London",
                          "Tokyo",
                          "Washington",
                          "Tokyo"
                      };
                      HashSet<string> hashSet = new HashSet<string>(cities);
                      foreach (var city in hashSet)
                      {
                          Console.WriteLine(city);
                      }
                  }
          

          當(dāng)你執(zhí)行完上面的程序,重復(fù)的城市名稱已經(jīng)被移除了。

          從 HashSet 中移除元素

          從HashSet 中刪除某一個元素可以調(diào)用 Remove 方法,它的語法結(jié)構(gòu)如下:

          
          public bool Remove (T item);
          

          如果在集合中找到了這個元素,Remove方法將會刪除這個元素并且返回true,否則返回 false。

          下面的代碼片段展示了如何使用 Remove 方法刪除 HashSet 中的元素

          
          string item = "D";
          if(hashSet.Contains(item))
          {
             hashSet.Remove(item);
          }
          

          如果你想刪除 HashSet 中的所有元素,可以調(diào)用 Clear 方法。

          HashSet 的 set操作

          HashSet提供了非常多的方法用于 set集合 操作上,比如說:IntersectWith, UnionWith, IsProperSubsetOf, ExceptWith, 和 SymmetricExceptWith

          IsProperSubsetOf

          這個 IsProperSubsetOf 用于判斷 HashSet 是否為某一個集合的完全子集,可以看下面的例子:

          
          HashSet<string> setA = new HashSet<string>() { "A", "B", "C", "D" };
          HashSet<string> setB = new HashSet<string>() { "A", "B", "C", "X" };
          HashSet<string> setC = new HashSet<string>() { "A", "B", "C", "D", "E" };
          if (setA.IsProperSubsetOf(setC))
             Console.WriteLine("setC contains all elements of setA.");
          if (!setA.IsProperSubsetOf(setB))
             Console.WriteLine("setB does not contains all elements of setA.");
          

          如果你執(zhí)行了上面這個程序,你會在控制臺上看到如下的輸出:

          UnionWith

          UnionWith方法常用于集合的合并,比如說下面的代碼:

          
          HashSet<string> setA = new HashSet<string>() { "A", "B", "C", "D", "E" };
          HashSet<string> setB = new HashSet<string>() { "A", "B", "C", "X", "Y" };
          setA.UnionWith(setB);
          foreach(string str in setA)
          {
             Console.WriteLine(str);
          }
          

          當(dāng)你執(zhí)行完上面的代碼,SetB 集合會被 SetA 集合吞掉,最后 SetA 集合將會是包括:"A", "B", "C", "D", "E", "X", and "Y"

          IntersectWith

          IntersectWith 方法常用于表示兩個 HashSet 的交集,下面的例子或許會讓你更加理解:

          
          HashSet<string> setA = new HashSet<string>() { "A", "B", "C", "D", "E" };
          HashSet<string> setB = new HashSet<string>() { "A", "X", "C", "Y"};
          setA.IntersectWith(setB);
          foreach (string str in setA)
          {
              Console.WriteLine(str);
          }
          

          當(dāng)你運行了上面的這段程序,只有兩個 HashSet 中都存在的元素才會輸出到控制臺中,輸出結(jié)果如下所示:

          ExceptWith

          ExceptWith 方法表示數(shù)學(xué)上的減法操作,這個時間復(fù)雜度是 O(N),假定你有兩個HashSet 集合,分別叫 setA 和 setB,并且用了下面的語句。

          
          setA.ExceptWith(setB);
          

          它返回的元素為: setA中有,setB中沒有 的最終結(jié)果,如果還不明白的話,使用如下代碼輔助理解:

          
          HashSet<string> setA = new HashSet<string>() { "A", "B", "C", "D", "E" };
          HashSet<string> setB = new HashSet<string>() { "A", "X", "C", "Y" };
          setA.ExceptWith(setB);
          foreach (string str in setA)
          {
             Console.WriteLine(str);
          }
          

          當(dāng)你執(zhí)行了上面這段程序,元素 B,D,E 將會輸出到控制臺上。

          SymmetricExceptWith

          SymmetricExceptWith 方法常用于修改一個 HashSet 來存放兩個 HashSet 都是唯一的元素,換句話說,我要的就是兩個集合都不全有的元素,如果還不明白的話,考慮下面的代碼段:

          
          HashSet<string> setA = new HashSet<string>() { "A", "B", "C", "D", "E" };
          HashSet<string> setB = new HashSet<string>() { "A", "X", "C", "Y" };
          setA.SymmetricExceptWith(setB);
          foreach (string str in setA)
          {
            Console.WriteLine(str);
          }
          

          當(dāng)你執(zhí)行完上面的代碼,你會發(fā)現(xiàn),setA中有而setB中沒有 和 setB中有而setA中沒有的元素將會輸出到控制臺中。

          我們知道數(shù)組的平均復(fù)雜度是 O(N),這里的 n 表示數(shù)組里的元素數(shù)量,而訪問 HashSet 中的某一個元素,它的復(fù)雜度為 O(1),這個常量復(fù)雜度就決定了 HashSet 在快速檢索 和執(zhí)行 set集合 操作上是一個非常好的選擇,你也可以使用 List 去存儲某些有指定順序的元素,同時也可以包含重復(fù)的值。

          更多高質(zhì)量干貨:參見我的 GitHub: dotnetfly**

          年的校招

          依稀記得,當(dāng)年我參加了大廠的校招,面試的是網(wǎng)易雷火工作室,當(dāng)時有一道題,我記得很清楚,就是:說說webpack中三種hash配置的區(qū)別

          哈哈,我當(dāng)時連webpack都不太會配置,所以也答不出來,然后也。。。沒有然后了。。

          哪三種?

          webpack中的三種hash分別是:

          • hash:全局hash
          • chunkhash:分組hash
          • contenthash:內(nèi)容hash

          實踐講解

          事先準(zhǔn)備

          準(zhǔn)備三個文件:

          • main.js
          import './main.css'
          
          console.log('我是main.js')
          • console.js
          console.log('我是console.js')
          • main.css
          .title {
            color: #000;
          }

          打包環(huán)境搭建

          打包環(huán)境的搭建我就不在這里詳細(xì)講了,想看的之后我會出一篇文章專門講解。這里我就抽取精華部分。

          • webpack.config.js
          // 多入口打包
          entry: {
              main: './src/main.js',
              console: './src/console.js'
            },
          // 輸出配置
          output: {
              path: path.resolve(__dirname, './dist'),
              // 這里預(yù)設(shè)為hash
              filename: 'js/[name].[hash].js',
              clean: true
            },
          plugins: [
                // 打包css文件的配置
                new MiniCssExtractPlugin({
                // 這里預(yù)設(shè)為hash
                filename: 'styles/[name].[hash].css'
              })
          ]

          hash

          由于我們預(yù)設(shè)的是hash,所以我們直接運行打包npm run build,我們看看我們打包后的是什么東西

          可以看到,所有文件的文件名hash值都是一致的,那我們現(xiàn)在改一下main.css這個文件

          .title {
            // #000 改成 #fff
            color: #fff;
          }

          然后我們再運行npm run build打包,看看打包后的是什么東西:

          可以看出,修改一個文件,所有文件的hash值跟著變

          結(jié)論:牽一發(fā)動全身,只改了一個main.css,會導(dǎo)致打包后所有文件的hash值都改變。所以當(dāng)打包名稱設(shè)置為hash時,整個項目文件是一致的,修改其中一個會導(dǎo)致所有跟著一起改。

          chunkhash

          我們把輸出文件名規(guī)則修改為chunkhash

          entry: {
              main: './src/main.js',
              console: './src/console.js'
            },
          output: {
              path: path.resolve(__dirname, './dist'),
              // 修改為 chunkhash
          修改    filename: 'js/[name].[chunkhash].js',
              clean: true
            },
          plugins: [
                new MiniCssExtractPlugin({
                // 修改為 chunkhash
          修改      filename: 'styles/[name].[chunkhash].css'
              })
          ]

          此時我們運行npm run build看看,打包后的東西:

          我們可以看出,hash值會根據(jù)入口文件的不同而分出兩個陣營:

          • main.js、main.css一個陣營,都屬于main.js入口文件
          • console.js一個陣營,屬于console.js入口文件

          那我們現(xiàn)在照樣修改一下main.css

          .title {
            // 從 #fff 改為 pink
            color: pink;
          }

          重新運行npm run build打包看看:

          可以看出,main.css修改后會影響main.css、main.js的hash值

          結(jié)論:當(dāng)規(guī)則為chunkhash時,打包后的hash值會根據(jù)入口文件的不用而不一樣,當(dāng)某個入口文件修改后重新打包,會導(dǎo)致本入口文件關(guān)聯(lián)的所有文件的hash值都修改,但是不會影響到其他入口文件的hash值

          contenthash

          我們把輸出文件名規(guī)則修改為contenthash

          entry: {
              main: './src/main.js',
              console: './src/console.js'
            },
          output: {
              path: path.resolve(__dirname, './dist'),
              // 修改為 contenthash
          修改    filename: 'js/[name].[contenthash].js',
              clean: true
            },
          plugins: [
                new MiniCssExtractPlugin({
                // 修改為 contenthash
          修改      filename: 'styles/[name].[contenthash].css'
              })
          ]

          運行npm run build打包,看看打包后的文件長什么樣子:

          可以看到,每個文件的hash值都不一樣,每個文件的hash值都是根據(jù)自身的內(nèi)容去生成的,那我們現(xiàn)在修改一下main.css

          .title {
            // pink 修改為 blue
            color: blue;
          }

          重新打包看看:

          可以看出,main.css修改后只會影響main.css得hash值,也就是自己的hash值

          結(jié)論:當(dāng)規(guī)則為contenthash時,每個文件的hash值都是根據(jù)自身內(nèi)容而生成,當(dāng)某個文件內(nèi)容修改時,打包后只會修改其本身的hash值,不會影響其他文件的hash值


          主站蜘蛛池模板: 亚洲AV综合色区无码一区爱AV| 欧洲亚洲综合一区二区三区| 香蕉久久av一区二区三区| 91在线精品亚洲一区二区| 一区二区三区免费视频网站| 99无码人妻一区二区三区免费| 日韩精品无码一区二区三区AV| 国产嫖妓一区二区三区无码| 久久青青草原一区二区| 极品人妻少妇一区二区三区| 台湾无码一区二区| 国产成人精品一区二区秒拍 | 99精品国产高清一区二区麻豆| 亚洲国产一区二区三区| 精品91一区二区三区| 亚洲欧美成人一区二区三区| 亚洲一区在线观看视频| 中文字幕在线一区二区在线| 日韩精品乱码AV一区二区| 无码人妻品一区二区三区精99 | 国偷自产视频一区二区久| 国产精品合集一区二区三区| 日本在线视频一区二区| 蜜臀AV在线播放一区二区三区| 日韩在线不卡免费视频一区| 无码AV天堂一区二区三区| 亚洲美女视频一区| 亚洲欧美日韩一区二区三区 | 日韩一区在线视频| 国产精品免费视频一区| 老鸭窝毛片一区二区三区| 相泽亚洲一区中文字幕| 久久久久人妻精品一区蜜桃| 精品亚洲AV无码一区二区三区| 亚洲国产成人精品无码一区二区| 无码国产精品一区二区免费式影视 | 精品一区二区三区在线观看视频 | 亚洲国产av一区二区三区丶| 国产精品一区二区不卡| 国产免费一区二区三区VR| 成人国内精品久久久久一区|