百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

遍历 Dictionary,你会几种方式?(遍历字典的key和value)

cac55 2024-10-11 10:51 24 浏览 0 评论

一:背景

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(#34;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<TKey, TValue>(reference.key, reference.value);
                return true;
            }
        }
    }

2. foreach 中 使用 KeyPairValue 解构

刚才你也看到了 item 是 KeyValuePair 类型,不过的是 netcore 对 KeyValuePair 进行了增强,增加了 Deconstruct 函数用来解构 KeyValuePair,代码如下:


    public readonly struct KeyValuePair<TKey, TValue>
    {
        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(#34;key={key},value={value}");
            }

3. foreach keys

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


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

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


public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>, 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<TKey, TValue>(reference.key, reference.value);
                return true;
            }
        }
        _index = _dictionary._count + 1;
        _current = default(KeyValuePair<TKey, TValue>);
        return false;
    }
}

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

三:使用 for 遍历

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


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

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

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


    public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index)
    {
        IList<TSource> list = source as IList<TSource>;
        if (list != null)
        {
            return list[index];
        }
        if (index >= 0)
        {
            using (IEnumerator<TSource> 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) ,好了,更多的 遍历方式 欢迎补充!

更多高质量干货:参见我的 GitHub: dotnetfly

相关推荐

博科矩阵新IP网络推动发展OTT服务

近日消息,日本电信服务提供商软银集团正在部署博科VDX交换机,为集团公司通用服务基础架构网络提供以太网矩阵,从而简化数据中心运营。这个新的基础架构将大幅度降低软银数据中心网络运营的复杂度和成本,使该公...

博科SDN战略落地 首款控制器Vyatta面世

ZDNET网络频道10月10日评论消息(文/于泽):虽然软件定义网络(SDN)近两年被炒得很热,但一直属于雷声大雨点小。各网络厂商都声称自家的交换机能够支持OpenFlow协议、实现SDN,不过就...

博科网络矩阵助Skilled Group“时刻在线”

澳大利亚最大的劳动力解决方案提供商SkilledGroup采用博科以太网和光纤通道存储区域网络(SAN)矩阵部署了一个创新的网络,从而打造了一个“时刻在线”的IT基础架构。博科矩阵实现了零停机环境以...

博科基于OpenDaylight推出SDN控制器Vyatta

ZDNET网络频道09月23日编译:博科周一宣布推出Vyatta控制器。Vyatta是博科SDN产品系列中一个新的主打产品。博科表示,Vyatta控制器是一步一步的从OpenDaylight项目中...

浏览器https方式访问博科FC光交显示没有匹配的加密算法套件

浏览器https方式访问博科FC光交显示没有匹配的加密算法套件报错的解决办法。。------------------------------------------------------------...

博科携手VMware推动软件定义数据中心和网络虚拟化的普及

2014年10月14日--博科(NASDAQ:BRCD)今天宣布,公司携手VMware,推出支持新IP的解决方案,以期让企业能够更轻松地迁移到软件定义数据中心(SDDC)和使用网络虚拟化。博科公司...

博科公司为追求速度极致的闪存拥趸提供光纤通道交换机

博科公司已经发布了一款每秒32Gbit第六代光纤通道交换机,这意味着其能够将现有每秒16Gbit连接速度提升一倍。其G620交换机采用1U机箱,提供24到64个端口,据博科方面所言这已经达到当前业...

博科推出第6代交换机 扩大光纤存储地位

博科今天宣布推出业内第一台第6代光纤通道存储网络交换机——博科G620,进一步扩大了博科在光纤通道技术领域的地位。这一全新专用且高密度SAN交换机提供突破性的性能和高可扩展性,旨在支持来自核心应用的数...

微信官宣新功能上线,聊天记录备份、迁移更好用了!

说到手机里哪个App最占空间,很多用户的答案大概都是微信,动辄占用几十甚至上百GB。不仅App本身体积庞大,更主要的是日积月累的聊天记录导致了空间的迅速消耗。此前,释放微信空间的常用方法是将...

局域网沟通工具--BeeBEEP(局域网内部聊天工具)

原文链接:局域网沟通工具--BeeBEEPHello,大家好啊!今天给大家带来一篇关于在信创终端上使用BeeBEEP的文章。BeeBEEP是一款安全、便捷的局域网即时通讯工具,支持文字聊天、文...

企业 IM 即时通讯底座,支持局域网通讯

在数字化浪潮下,企业对即时通讯的需求日益增长,尤其是对通讯安全性、可控性的要求愈发严苛。BeeWorks作为专业的企业IM即时通讯底座,凭借对局域网通讯的支持,为企业打造了优质可控的即时通讯与实...

IM即时通讯软件,构建企业局域网内安全协作

安全与权限:协同办公的企业级保障在协同办公场景中,BeeWorks将安全机制贯穿全流程。文件在局域网内传输与存储时均采用加密处理,企业网盘支持水印预览、离线文档权限回收等功能,防止敏感资料外泄;多人...

当今信息化时代都离不开WLAN, 今天给大家普及一下WLAN知识

无线局域网(WirelessLocalAreaNetworks/WLAN)一.无线让网络使用更自由:1.凡是自由空间均可连接网络,不受限于线缆和端口位置。二.无线让网络建设更经济:1.终端...

软网推荐:寻找WebQQ替代者 在线可以继续聊

不少公司禁止上班聊天,常常采取封禁QQ、关闭端口等方法,导致很多聊天软件无法使用。以前我们可以通过WebQQ绕开限制,不过WebQQ在2019年1月1日开始停止服务,想要继续隐蔽聊天,就只能找其他一些...

搭建自己的聊天室平台、公司内部聊天平台,Rocket.Chat搭建使用

一,简介rocket.chat是一个开源的社交软件,即可以直接在web页面使用,也可以下载APP(Android,IOS,Windows,MacOS)主要功能:群组聊天,直接通信,私聊群,桌面通知...

取消回复欢迎 发表评论: