<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          遍歷 Dictionary,你會(huì)幾種方式?

          共 3241字,需瀏覽 7分鐘

           ·

          2020-10-07 16:24

          一:背景

          1. 講故事

          昨天在 StackOverflow 上看到一個(gè)很有趣的問題,說: 你會(huì)幾種遍歷字典的方式,然后跟帖就是各種奇葩的回答,挺有意思,馬上就要國(guó)慶了,娛樂娛樂吧,說說這種挺無聊的問題???。

          二:使用 foreach 遍歷

          為了方便演示,先上一段測(cè)試代碼:


          ????????????var?dict?=?new?Dictionary<int,?string>()
          ????????????{
          ????????????????[10]?=?"A10",
          ????????????????[20]?=?"A20",
          ????????????????[30]?=?"A30",
          ????????????????[40]?=?"A40",
          ????????????????[50]?=?"A50"
          ????????????};

          1. 直接 foreach dict

          如果要拿百分比說話,估計(jì)有 50%+ 的小伙伴用這種方式,為啥,簡(jiǎn)單粗暴唄,其他沒什么好說的,直接上代碼:


          ????????????foreach?(var?item?in?dict)
          ????????????{
          ????????????????Console.WriteLine($"key={item.Key},value={item.Value}");
          ????????????}

          這里的 item 是底層在 MoveNext 的過程中用 KeyValuePair 包裝出來的,如果你不信的話,看下源碼唄:


          ?public?bool?MoveNext()
          ?{
          ??while?((uint)_index?uint)_dictionary._count)
          ??{
          ???ref?Entry?reference?=?ref?_dictionary._entries[_index++];
          ???if?(reference.next?>=?-1)
          ???{
          ????_current?=?new?KeyValuePair(reference.key,?reference.value);
          ????return?true;
          ???}
          ??}
          ?}

          2. foreach 中 使用 KeyPairValue 解構(gòu)

          剛才你也看到了 item 是 KeyValuePair 類型,不過??的是 netcore 對(duì) KeyValuePair 進(jìn)行了增強(qiáng),增加了 Deconstruct 函數(shù)用來解構(gòu) KeyValuePair,代碼如下:


          ????public?readonly?struct?KeyValuePair
          ????{
          ????????private?readonly?TKey?key;

          ????????private?readonly?TValue?value;

          ????????public?TKey?Key?=>?key;

          ????????public?TValue?Value?=>?value;

          ????????public?KeyValuePair(TKey?key,?TValue?value)
          ????????{
          ????????????this.key?=?key;
          ????????????this.value?=?value;
          ????????}

          ????????public?void?Deconstruct(out?TKey?key,?out?TValue?value)
          ????????{
          ????????????key?=?Key;
          ????????????value?=?Value;
          ????????}
          ????}

          有了這個(gè)解構(gòu)函數(shù),你就可以在遍歷的過程中直接拿到 key,value,而不是包裝的 KeyValuePair,這在 netframework 中可是不行的哈,實(shí)現(xiàn)代碼如下:


          ????????????foreach?((int?key,?string?value)?in?dict)
          ????????????{
          ????????????????Console.WriteLine($"key={key},value={value}");
          ????????????}

          3. foreach keys

          前面的例子都是直接對(duì) dict 進(jìn)行 foreach,其實(shí)你還可以對(duì) dict.keys 進(jìn)行 foreach 遍歷,然后通過遍歷出的 key 對(duì) dict 進(jìn)行類索引器讀取,代碼如下:


          ????????????foreach?(var?key?in?dict.Keys)
          ????????????{
          ????????????????Console.WriteLine($"key={key},value={dict[key]}");
          ????????????}

          說到這里,不知道你是否有一個(gè)潛意識(shí),那就是 dict 只能通過 foreach 進(jìn)行遍歷,真相是不是這樣的呢?要尋找答案,還是回頭看一下 foreach 是如何進(jìn)行遍歷的。


          public?struct?Enumerator?:?IEnumerator>,?IDisposable,?IEnumerator,?IDictionaryEnumerator
          {
          ????public?bool?MoveNext()
          ?{
          ??while?((uint)_index?uint)_dictionary._count)
          ??{
          ???ref?Entry?reference?=?ref?_dictionary._entries[_index++];
          ???if?(reference.next?>=?-1)
          ???{
          ????_current?=?new?KeyValuePair(reference.key,?reference.value);
          ????return?true;
          ???}
          ??}
          ??_index?=?_dictionary._count?+?1;
          ??_current?=?default(KeyValuePair);
          ??return?false;
          ?}
          }

          仔細(xì)看這個(gè) while 循環(huán),你就應(yīng)該明白,本質(zhì)上它也是對(duì) entries 數(shù)組進(jìn)行遍歷,那底層都用了 while,我是不是可以用 for 來替換然后循環(huán) dict 呢?哈哈,反正就是模仿唄。

          三:使用 for 遍歷

          為了把 MoveNext 中的代碼模擬出來,重點(diǎn)在于這條語句:ref Entry reference = ref _dictionary._entries[_index++];, 其實(shí)很簡(jiǎn)單,_entries 數(shù)組內(nèi)容的提取可以用 Linq 的 ElementAt 方法,是不是~~~ ,改造后的代碼如下:


          ????????????for?(int?i?=?0;?i?????????????{
          ????????????????(int?key,?string?value)?=?dict.ElementAt(i);

          ????????????????Console.WriteLine($"key={key},value={dict[key]}");
          ????????????}

          接下來是不是很好奇這個(gè) ElementAt 擴(kuò)展方法是如何實(shí)現(xiàn)的,一起看看源碼吧。


          ????public?static?TSource?ElementAt(this?IEnumerable?source,?int?index)
          ????{
          ????????IList?list?=?source?as?IList;
          ????????if?(list?!=?null)
          ????????{
          ????????????return?list[index];
          ????????}
          ????????if?(index?>=?0)
          ????????{
          ????????????using?(IEnumerator?enumerator?=?source.GetEnumerator())
          ????????????{
          ????????????????while?(enumerator.MoveNext())
          ????????????????{
          ????????????????????if?(index?==?0)
          ????????????????????{
          ????????????????????????return?enumerator.Current;
          ????????????????????}
          ????????????????????index--;
          ????????????????}
          ????????????}
          ????????}
          ????}

          從上面代碼可以看到,如果當(dāng)前的 source 沒有實(shí)現(xiàn) IList 接口的話,那就是一個(gè)巨大的坑,每一次執(zhí)行 ElementAt 方法,最壞時(shí)間復(fù)雜度都是 O(N),就拿剛才的 for循環(huán)來說,它的最壞時(shí)間復(fù)雜度就是 O(n!) ,是不是比你想象的要恐怖的多,教訓(xùn)就是多實(shí)踐,多看看源碼~

          四:總結(jié)

          這篇列舉了 4 種遍歷 dict 的方式,不知你會(huì)用到哪幾種?要注意的是最后 ElementAt 對(duì) Source 判別上的大坑一定要明白,不要想當(dāng)然的以為就是 O(N) ,好了,更多的 遍歷方式 歡迎補(bǔ)充!


          瀏覽 25
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久久成人片免费观看蜜芽 | 国产的色情电影在线播放 | 黄色无遮挡亚洲 | 日韩欧美网站 | 操婷婷逼|