C#从零开始学习笔记---第十三天(续)
在上一篇的最后我们简单介绍了一下List动态数组,他适合一堆有序的,同类型的数据。对照的是我们之前记录的ArrayList。那我们之前还记录过一种结构叫哈希表,不知道大家还记得不记得,那是一种键值对类型,一一对应的,那有没有一种泛型集合也是存储这种键值对类型的数据呢?有的兄弟,有的。他的名字就叫做字典---Dictionary<Tkey,Tvalue>。
1.什么是字典
在C#里字典就是一个“对照表”。存的是“键(key)--->值(value)”的这种一一对应的这种关系,通过键可以快速找到对应的值。他之所以叫字典就是因为他就像平时我们用到的字典一样,键就相当于我们查的汉字,每个字都不一样,不能重复;值就像我们查字典的索引方式一样,不管是拼音还是部首,亦或者是笔画,是可以重复的。
字典有以下优点:第一,查询极快:通过 Key 找 Value 几乎是瞬间完成,比 List 遍历快得多;第二,键唯一:同一个 Key 只能对应一个 Value,不会混乱;第三,动态扩容:和 List 一样,不用提前指定大小。
2.字典的定义和初始化
// 1. 定义一个空字典:key是字符,value是整数
Dictionary<char, int> charCount = new Dictionary<char, int>();
// 2. 定义并直接初始化字典
Dictionary<string, int> fruitPrice = new Dictionary<string, int>()
{
{ "苹果", 5 },
{ "香蕉", 3 },
{ "橙子", 4 }
};
// 3. 获取字典中键值对的个数(同样用Count,不用Length!)
Console.WriteLine("初始化后字典大小:" + fruitPrice.Count);
在上面代码里,我们可以看到在定义一个字典的时候我们需要给它两个类型的变量,第一个类型是我们键的类型,第二个类型是我们值的类型。运行结果如下:

这里再强调一下,字典里的键必须唯一,不能重复;字典的值是可以重复的;同时所有集合都用Count获取元素的个数,只有数组才用Length。
3.字典中的常用操作
3.1 添加键值对
在初始化的时候我们定义了一个水果价格的字典,里面已经有了苹果,香蕉和橙子的价格,我们接着对这个字典进行操作:
// 方式1:Add方法(推荐,明确是添加)
fruitPrice.Add("葡萄", 8);
// 方式2:索引赋值
// 如果Key不存在,就是添加;如果Key已经存在,就是修改!
fruitPrice["西瓜"] = 2;
Console.WriteLine("\n=== 添加元素后 ===");
foreach (var item in fruitPrice)
{
Console.WriteLine($"{item.Key}:{item.Value}元");
}
在这里我们介绍了两种添加方式,第一种其实和之前讲的集合和列表的添加方式差不多,就是用字典名打点add添加就行。其中括号里第一个元素是我们的键,第二个是我们的值。这种赋值方式的好处是如果键已经存在,就无法在进行赋值,系统会直接报错。第二种方式就是我们通过索引进行赋值,但是这种索引方式就存在缺陷,因为如果我们的键已经存在的话,这个赋值就会覆盖掉我们原本的值,不会提醒我们键已经存在,有可能导致我们后续进程的错误。
上面代码添加完的运行结果如下:

3.2 查找一个值
因为我们的字典设置的有键嘛,所以我们在字典里查找值可以直接根据键来查找,但这时候我们需要预防这个键在不在我们创建的字典里。我们来看代码:
// 方式1:直接用索引获取(不推荐!)
// 如果Key不存在,直接报“给定关键字不在字典中”错误
int applePrice = fruitPrice["苹果"];
Console.WriteLine("\n苹果的价格:" + applePrice);
//Console.WriteLine(fruitPrice["芒果"]);
// 方式2:TryGetValue方法(强烈推荐!)
// 如果Key存在,返回true,并把值赋给out参数
// 如果Key不存在,返回false,不会报错
if (fruitPrice.TryGetValue("芒果", out int mangoPrice))
{
Console.WriteLine("芒果的价格:" + mangoPrice);
}
else
{
Console.WriteLine("芒果不在字典中");
}
可以看到,这里我在上面的代码里注掉了一句打印芒果价格的语句,因为我们的字典里没有芒果,所以我们如果直接打印的话系统就会给我们抛出一个异常导致我们运行失败,如下图:

这时就需要我们进行第二种操作了,我们可以使用TryGetValue的方式来进行查找,这里用到了我们之前提到的out关键字,让他往外输出一个值,然后做出判断,如果存在就打印,如果不存在就提醒用户不存在。运行结果如下:

3.3 修改一个值
当我们在字典里修改一个值的时候相对而言就比较简单了,直接通过键来修改。
// 用索引赋值,Key存在就是修改
Console.WriteLine("\n=== 修改香蕉价格前 ===");
Console.WriteLine($"香蕉:{fruitPrice["香蕉"]}元");
fruitPrice["香蕉"] = 4;
Console.WriteLine("\n=== 修改香蕉价格后 ===");
Console.WriteLine($"香蕉:{fruitPrice["香蕉"]}元");
我们来看结果:

当然这种情况再添加的时候我也提到了,你要防止键不存在,如果键不存在这里就不是修改了,就变成添加了。
3.4 判断键是否存在
在C#里我们判断字典的键是否存在可以直接用系统里内置的ContainsKey来进行判断,他返回的是一个bool类型的变量。如果存在,就返回true,不存在就返回false。
if (fruitPrice.ContainsKey("橙子"))
{
Console.WriteLine("\n字典中有橙子");
}
if (!fruitPrice.ContainsKey("芒果"))
{
Console.WriteLine("\n字典中没有芒果");
}
在上面代码里,我们第一个if判断因为字典里有橙子嘛,所以他的返回值就为true,所以就会打印字典里有橙子。但在第二个条件判断里,因为字典里不存在芒果,所以这里就返回false,所以我们就加一个!,让他返回值变成true,这样就能输出字典里存不存在芒果。结果如下:

3.5 删除键值对
在字典里删除我们一般使用内置方法Remove,这个方法使用过键来进行删除,返回值的类型也是一个bool类型,如果删除成功就返回true,失败就返回false。
// Remove方法:按Key删除,返回bool表示是否删除成功
bool isRemoved1 = fruitPrice.Remove("西瓜");
Console.WriteLine($"\n是否删除了西瓜:{isRemoved1}");
bool isRemoved2 = fruitPrice.Remove("芒果");
Console.WriteLine($"\n是否删除了芒果:{isRemoved2}");
Console.WriteLine("\n=== 删除西瓜后 ===");
foreach (var item in fruitPrice)
{
Console.WriteLine($"{item.Key}:{item.Value}元");
}
我们来看一下结果:

在这里面我们可以看到,因为我们原来字典里有西瓜这个键,所以我们就能成功删除,但没有芒果,所以就删除不了。
3.6 字典的遍历
因为字典里存储的一对对键值对嘛,所以我们再遍历的时候就会有三种方式:第一种,直接遍历键值对;第二种,只遍历所有的键;第三种,只遍历所有的值。我们来看看代码:
// 方式1:遍历所有键值对(最常用)
Console.WriteLine("\n=== 方式1:遍历键值对 ===");
foreach (var item in fruitPrice)
{
Console.WriteLine($"{item.Key}:{item.Value}元");
}
// 方式2:只遍历所有Key
Console.WriteLine("\n=== 方式2:遍历所有Key ===");
foreach (string key in fruitPrice.Keys)
{
Console.WriteLine(key);
}
// 方式3:只遍历所有Value
Console.WriteLine("\n=== 方式3:遍历所有Value ===");
foreach (int value in fruitPrice.Values)
{
Console.WriteLine(value + "元");
}
我们一般最常用的是直接遍历所有键值对,因为这样我们既能看到键又能看到值,我们直接来看结果:

4.总结
这里我们来说一下在使用字典里需要注意的一些情况:
第一,键值不能重复,同一个键只能添加一次,如果重复添加就会报错。如果我们不确定键值是否存在,我们可以先用ContainsKey进行判断,或者直接用索引赋值。
第二,键值不存在的时候如果索引就会直接报错。在平时我们尽量不要直接写“字典名[键]”来获取值,除非我们能百分百确定这个键存在。正常情况下我们要用TryGetValue这种方法,看起来会比较专业一点。
第三,在遍历字典的时候我们是不能对字典进行修改的,如果在遍历过程中修改,就会出现遍历出问题的这种情况。
第四,因为我们的键的类型和值的类型都是我们自己定义的嘛,所以当我们键的类型是引用类型,如string时,这时候键的类型是可以为空的;当我们键的类型是值类型的时候,如int,char时,这时候键的类型是不能为空的。
ok啊,最后给大家留一道题,大家来体会体会:
例题:统计一个字符串每个字符出现的次数,以及谁的次数最多 要求使用字典?
这里我就不讲解思路了,大家自己思考一下,然后我把我做的代码附上,大家可以参考:
string s = "abcabcaabc";
Dictionary<char, int> dic = new Dictionary<char, int>();
for(int i = 0; i < s.Length; i++)
{
if (!dic.ContainsKey(s[i]))
{
dic.Add(s[i], 1);
}
else
{
dic[s[i]]++;
}
}
foreach(var item in dic)
{
Console.WriteLine(item.Key+" "+item.Value);
}
char maxChar = ' ';
int maxCount = 0;
foreach (var item in dic)
{
if (item.Value > maxCount)
{
maxCount = item.Value;
maxChar = item.Key;
}
}
Console.WriteLine($"\n出现最多的字符是:{maxChar},共 {maxCount} 次");
ok啊,这期内容就到这里了,大家好好消化消化,我们下期再见!!
更多推荐
所有评论(0)