一:背景

1. 讲故事

昨天在 StackOverflow 上看到一个很有趣的问题,说: 你会几种遍历字典的方式,然后跟帖就是各种奇葩的回答,挺有意思,马上就要国庆了,娱乐娱乐吧,说说这种挺无聊的问题???。

二:使用 foreach 遍历

为了方便演示,先上一段测试代码:

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

1. 直接 foreach dict

如果要拿百分比说话,估计有 50%+ 的小伙伴用这种方式,为啥,简单粗暴呗,其他没什么好说的,直接上代码:

            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 解构

刚才你也看到了 item 是 KeyValuePair 类型,不过??的是 netcore 对 KeyValuePair 进行了增强,增加了 Deconstruct 函数用来解构 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;        }    }

有了这个解构函数,你就可以在遍历的过程中直接拿到 key,value,而不是包装的 KeyValuePair,这在 netframework 中可是不行的哈,实现代码如下:

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


3. foreach keys

前面的例子都是直接对 dict 进行 foreach,其实你还可以对 dict.keys 进行 foreach 遍历,然后通过遍历出的 key 对 dict 进行类索引器读取,代码如下:

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


说到这里,不知道你是否有一个潜意识,那就是 dict 只能通过 foreach 进行遍历,真相是不是这样的呢?要寻找答案,还是回头看一下 foreach 是如何进行遍历的。

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

仔细看这个 while 循环,你就应该明白,本质上它也是对 entries 数组进行遍历,那底层都用了 while,我是不是可以用 for 来替换然后循环 dict 呢?哈哈,反正就是模仿呗。

三:使用 for 遍历

为了把 MoveNext 中的代码模拟出来,重点在于这条语句:ref Entry reference = ref _dictionary._entries[_index++];, 其实很简单,_entries 数组内容的提取可以用 Linq 的 ElementAt 方法,是不是~~~ ,改造后的代码如下:

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

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


接下来是不是很好奇这个 ElementAt 扩展方法是如何实现的,一起看看源码吧。

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

从上面代码可以看到,如果当前的 source 没有实现 IList 接口的话,那就是一个巨大的坑,每一次执行 ElementAt 方法,最坏时间复杂度都是 O(N),就拿刚才的 for循环来说,它的最坏时间复杂度就是 O(n!) ,是不是比你想象的要恐怖的多,教训就是多实践,多看看源码~

四:总结

这篇列举了 4 种遍历 dict 的方式,不知你会用到哪几种?要注意的是最后 ElementAt 对 Source 判别上的大坑一定要明白,不要想当然的以为就是 O(N) ,好了,更多的 遍历方式 欢迎补充!

c标签foreach遍历list_遍历 Dictionary,你会几种方式?相关推荐

  1. jstl标签forEach的用法--遍历java的集合

    再讲<c:forEach>之前,现讲一下让EL表达式生效的语句 <% @ page isELIgnored="false"%>这句语句在你想让EL表达式生效 ...

  2. Java中遍历Map集合的5种方式总结

    这篇文章主要给大家介绍了关于Java中遍历Map集合的5种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值. 方式一 通过Map.keySet使用iterator遍历 ...

  3. 【C语言】二维数组遍历的3种方式

    #include <math.h> #include <stdio.h> #include <stdlib.h>#define ROW 2 #define COL ...

  4. foreach 实现 MyBatis 遍历集合与批量操作数据

    一.写在前面 MyBatis 动态 SQL 的一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候.foreach允许你指定一个集合,声明可以在元素体内使用的集合项(item)和 ...

  5. java for数组遍历数组_Java foreach操作(遍历)数组

    语法: 我们分别使用 for 和 foreach 语句来遍历数组 运行结果: 练习: import java.util.Arrays; public class HelloWorld { public ...

  6. 原生JS forEach()和map()遍历的区别以及兼容写法

    一.原生JS forEach()和map()遍历 共同点: 1.都是循环遍历数组中的每一项. 2.forEach() 和 map() 里面每一次执行匿名函数都支持3个参数:数组中的当前项item,当前 ...

  7. Java中forEach, 用来遍历数组

    这里的for是Java中forEach, 用来遍历数组的.for(int i : d) 就是遍历int型数组d的 每一次访问数组d的时候读取的数据放入int型的i中.和for(int i=0;i< ...

  8. foreach lambda写法_Java8新特性之forEach+Lambda 表达式遍历Map和List

    这是Java8系列的第二篇,今天来说一下Java8中forEach的简单使用.我们使用对比的方式来看应该会看得更加清楚,更能理解: 一.遍历Map ============Java8之前的方式==== ...

  9. JavaScript中遍历数组的for for-in和forEach三种方式

    JavaScript中遍历数组的for for-in和forEach三种方式 for循环 let arr = [1,2,3,4,5,6];for(let i = 0; i < arr.lengt ...

最新文章

  1. DNC-cs6200 ospfv3
  2. 终端文件夹跳转工具autojump
  3. grabcut.cpp:380: error: (-215) !bgdSamples.empty() !fgdSamples.empty() in function initGMMs
  4. 科大讯飞语音引擎_科大讯飞的1024:语音技术进一步突破,发布专用芯片
  5. php excel 导入 显示,php Excel 导入
  6. XML 命名空间以及它们如何影响 XPath 和 XSLT (Extreme XML)
  7. node-mysql_Nodejs与MySQL交互(felixge/node-mysql)
  8. RegisterWaitForSingleObject的使用
  9. 95社区(对接第三方社区)
  10. 苹果如何将图片转换为文字手机
  11. java五子棋技术路线,一位老码农的编程简史
  12. 电脑文件管理——XYplorer
  13. PV,V,UV的概念,采集数据
  14. 众里寻他千百度,不如用它来搜库!
  15. java postgresql date_javapostgresql时区总结
  16. 机器学习算法工程师到底应该学哪个编程语言?
  17. 浙江大学计财处预约报销流程
  18. Alex Woodie:2019大数据预测
  19. 最近最开心的一件事情
  20. onegreen的绿软word2003绿色版删除不掉的解决方案

热门文章

  1. 注入器 过检测_连云港管道检测服务
  2. 计算机应用基础人才培养方案,1. 培养方案(计算机应用基础课程).doc
  3. 转译和编译_10个有趣又能编译为JavaScript的语言,你用过哪些?
  4. 光纤收发器长距离的传输过程出现死机的解决方案
  5. 【渝粤题库】陕西师范大学200681C语言程序设计 作业(高起专、高起本)
  6. 【渝粤题库】广东开放大学 会展概论 形成性考核
  7. sougou ubuntu 优麒麟_搜狗输入法 Linux – V2.3 版发布,完美适配优麒麟 19.10
  8. 基于java家教管理系统_基于jsp的家教信息管理-JavaEE实现家教信息管理 - java项目源码...
  9. C++判断是否为素数、求一个数的因数、质因数分解
  10. 图神经网络PGL助力国民级音乐App,创新迭代千亿级推荐系统(人工智能应用案例)