java dictionary遍历_遍历 Dictionary,你会几种方式?

一:背景

1. 讲故事

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

二: 使用 foreach 遍历

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

var dict = new Dictionary()

{

[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}");

}

1bc97e1662346d31318536d2b1aa17cf.png

这里的 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}");

}

b5bc525b115c143444c1ce3bf75445cf.png

3. foreach keys

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

foreach (var key in dict.Keys)

{

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

}

064ba6557ed3c89a71f0615f39473439.png

说到这里,不知道你是否有一个潜意识,那就是 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 < dict.Count; i++)

{

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

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

}

b5bc525b115c143444c1ce3bf75445cf.png

接下来是不是很好奇这个 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) ,好了,更多的 遍历方式 欢迎补充!

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/426083.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SP26073】DIVCNT1 - Counting Divisors 题解

题目描述 定义 \(d(n)\) 为 \(n\) 的正因数的个数&#xff0c;比如 \(d(2) 2, d(6) 4\)。 令 $ S_1(n) \sum_{i1}^n d(i) $ 给定 \(n\)&#xff0c;求 \(S_1(n)\)。 输入格式 第一行包含一个正整数 \(T\) (\(T \leq 10^5\))&#xff0c;表示数据组数。 接下来的 \(T\) 行&am…

密码系统的安全性

1&#xff0c;评估密码系统安全性主要有三种方法&#xff1a; &#xff08;1&#xff09;无条件安全性 这种评价方法考虑的是假定攻击者拥有无限的计算资源&#xff0c;但仍然无法破译该密码系统。 &#xff08;2&#xff09;计算安全性 这种方法是指使用目前最好的方法攻破…

java学习(135):map中泛型使用

定义一个员工类 public class Employee {private String name;private String ags;public void setName(String name) {this.name name;}public String getName() {return name;}public void setAgs(String ags) {this.ags ags;}public String getAgs() {return ags;} }定义…

java两种绑定方式_Javascript绑定事件的两种方式的区别

命名函数function check(){//code}匿名函数window.onload function(){//先获取元素对象&#xff0c;再绑定事件&#xff0c;绑定的是匿名函数不可重用var btn document.getElementById("btn");btn.onclick function(){//code}}以前一直以为两种方式的区别不大&…

前端基础_认识前端.md

前端学习 前端学习路线学习网站 菜鸟驿站慕课网freeCOdeCampw3schooltry8在线编辑 codepenjsfiddlethecodeplayer其他网站 cssfilterscssstats极客学院搭建个人博客wordpress博客园网站检查规范How to learn webTobe continue... 学习准备 查看浏览器占有的市场份额 查看浏览器…

[剑指offer][JAVA][第62题][约瑟夫环][LinkedList vs ArrayList]

【问题描述】 面试题62. 圆圈中最后剩下的数字 0,1,,n-1这n个数字排成一个圆圈&#xff0c;从数字0开始&#xff0c;每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。例如&#xff0c;0、1、2、3、4这5个数字组成一个圆圈&#xff0c;从数字0开始每次删除第…

java创建两个foo方法_Java类实例化原理 - osc_foo7glsg的个人空间 - OSCHINA - 中文开源技术交流社区...

Java对象的创建过程包括类初始化(类实例化两个阶段。一、Java对象创建时机(1)使用new关键字创建对象(2)反射创建对象使用Class类的newInstance方法Student student2 (Student)Class.forName("Student类全限定名").newInstance()&#xff1b;使用Constructor类的newI…

java学习(136):带泛型的类

SuppressWarnings("all") public class GJClass<T> {public String getClassName(T t){return t.getClass().getName();} } 测试类 public class test76 {public static void main(String[] args){GJClass gjClassnew GJClass();String classNamegjClass.get…

如何往eclipse中导入maven项目

现在公司中大部分项目可能都是使用maven来构建&#xff0c;假如现在摆在你面前有一个maven的项目&#xff0c;如果你要学习它&#xff0c;如何将它导入到像eclipse这样的集成开发工具中呢&#xff0c;以项目public_class_1为例&#xff1a; 1.在eclipse的工作界面的最左侧&…

[Leetcode][JAVA][第912题][排序算法]

【问题描述】 给你一个整数数组 nums&#xff0c;将该数组升序排列。 示例 1&#xff1a; 输入&#xff1a;nums [5,2,3,1] 输出&#xff1a;[1,2,3,5]【解答思路】 1.插入排序&#xff08;熟悉&#xff09; 每次将一个数字插入一个有序的数组里&#xff0c;成为一个长度更…

java 调用r语言包传参数_Java与R语言的配置,调用

我是最近才接触到了R语言&#xff0c;所以用起来有很多的问题&#xff0c;之前只是想单纯想用java调用到R语言中的一些东西&#xff0c;没有想到这个事情并不是想象的那么简单的。好了&#xff0c;闲话不多说&#xff0c;下面我来说说我在运用R的时候遇上的问题吧。第一步&…

玩转oracle 11g(42):增加表空间

--查询表空间 select t.tablespace_name, d.file_name, d.autoextensible, d.maxbytes, d.status from dba_tablespaces t, dba_data_files d where t.tablespace_name d.tablespace_name order by tablespace_name.file_name; --增加表空间 AL…

php下载文件添加header响应头

header(Content-type:application/octet-stream);header(Content-Disposition:attachment;filename".basename($file).");header(Content-Length:.filesize($file));readfile($file);转载于:https://www.cnblogs.com/jielin/p/10203140.html

[Leetcode][JAVA][第1111题][栈思想]

【问题描述】 有效括号字符串 定义&#xff1a;对于每个左括号&#xff0c;都能找到与之对应的右括号&#xff0c;反之亦然。详情参见题末「有效括号字符串」部分。嵌套深度 depth 定义&#xff1a;即有效括号字符串嵌套的层数&#xff0c;depth(A) 表示有效括号字符串 A 的嵌…

玩转oracle 11g(43):oracle导出空表

因为11G数据库在CREATE表后数据库不会立刻给该表分配物理存储空间&#xff0c;所以导出数据库的时候自然而然不会导出该表。 解决方案&#xff1a;在导出表服务器上找出所有数据为空的表&#xff0c;批处理的给没有数据行的数据表分配存储空间。 方法1.此为分步骤执行&#x…

分类器交叉验证java_使用交叉验证的KNN分类器

首先&#xff0c;您需要准确定义您的任务 . F.ex给出R ^(MxN)中的图像I&#xff0c;我们希望将I分类为包含面部的图像或没有面部的图像 .我经常使用像素分类器&#xff0c;其任务类似于&#xff1a;对于图像&#xff0c;我决定每个像素是面像素还是非面像素 .定义任务的一个重要…

Python——assert(断言函数)

一、断言函数的作用 python assert断言是声明其布尔值必须为真的判定&#xff0c;如果发生异常就说明表达示为假。可以理解assert断言语句为raise-if-not&#xff0c;用来测试表示式&#xff0c;其返回值为假&#xff0c;就会触发异常。 二、常用格式 assert 11  assert 222*…

[Leetcode][JAVA][第20题][Stack][Map]

【问题描述】 20. 有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串&#xff0c;判断字符串是否有效。有效字符串需满足&#xff1a;左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 注意空字符串可…

java学习(137):java异常初识

//java异常初识 public class test78 {public static void main(String[] args) {countArraylength( -1 );}public static int countArraylength(int length) {int[] nums new int[length];return nums.length;} } 运行结果

Java如何随机出石头剪刀布_JAVA编程实现石头剪刀布

我不是焊工import java.util.Scanner;public class Jsb {public static void main(String[] args) {while (true) {result(input(), random());System.out.println("");}}public static int input() {System.out.println("请输入&#xff1a;1-剪刀&#xff0c;…