C#核心学习(三)常见的泛型数据结构类(1)List和Dictionary
前面我们刚刚学习了,什么是泛型。今天我们就来看看C#中有哪些,常见的泛型数据结构,今天要介绍的是List,和Dictionary。
引言
在C#编程中,泛型集合是高效管理数据的核心工具。List<T>
和Dictionary<TKey, TValue>
作为两种最常用的泛型数据结构,分别解决了动态数组管理和键值对快速查找的核心需求。List<T>
以动态扩容和有序存储为特点,支持灵活的索引操作和批量处理;Dictionary<TKey, TValue>
则基于哈希表实现,通过唯一键实现近乎瞬时的数据检索。理解它们的特性、方法及适用场景,是提升代码性能和可维护性的关键。本文将系统解析这两种集合的核心功能、API及实践技巧,帮助开发者合理选择工具,优化数据处理逻辑。
1. List<T>(动态数组)
前面我们有了静态数组,还有一个可以自动扩容的数组ArrayList,今天我们学习一个更加泛华的动态数组
List是一个C#为我们封装好的类
它的本质是一个可变类型的泛型数组
List类帮助我们实现了很多方法
比如泛型数组的增删查改
特性
动态大小:自动调整容量以容纳新增元素。
有序集合:元素按插入顺序存储,支持索引访问(如
list[0]
)。允许重复元素。
基于数组实现:支持快速随机访问(时间复杂度为 O(1))。
申明一个List出来:
注意:需要引入命名空间:using System.Collections.Generic;
// 空列表
List<int> numbers = new List<int>();
List<string> names = new List<string>();// 带初始容量(优化性能)
List<double> values = new List<double>(100);// 初始化时添加元素
List<char> chars = new List<char> { 'a', 'b', 'c' };
常用方法
添加元素:
list.Add(item); // 添加到末尾
list.Insert(index, item); // 插入到指定位置
增加也可以添加一个列表进去:利用AddRange
List<string> liststr = new List<string>();
list2.AddRange(liststr);
删除元素:
list.Remove(item); // 删除第一个匹配项
list.RemoveAt(index); // 删除指定索引的元素
list.Clear(); // 清空所有元素
查找元素:
bool exists = list.Contains(item); // 检查是否存在
int index = list.IndexOf(item); // 获取元素索引
关于查找:
查找索引:
IndexOf(T item)
返回第一个匹配项的索引,未找到返回 -1
。
int index = list.IndexOf("apple");
LastIndexOf(T item)
返回最后一个匹配项的索引。
int lastIndex = list.LastIndexOf("apple");
FindIndex(Predicate<T> match)
根据条件查找第一个匹配项的索引。
int index = list.FindIndex(x => x.StartsWith("A"));
FindLastIndex(Predicate<T> match)
根据条件查找最后一个匹配项的索引。
int lastIndex = list.FindLastIndex(x => x > 100);
BinarySearch(T item)
使用二分查找(要求列表已排序),返回索引或负数(未找到)。
list.Sort();
int index = list.BinarySearch("apple");
Exists(Predicate<T> match)
检查是否存在符合条件的元素。
bool exists = list.Exists(x => x == 42);
容量管理:
list.Capacity = 100; // 预分配容量(减少扩容次数)
遍历相关:
使用迭代器(foreach
):
foreach (int num in numbers)
{Console.WriteLine(num);
}
不使用迭代器(for
循环):
for (int i = 0; i < numbers.Count; i++)
{Console.WriteLine(numbers[i]);
}
性能
添加/删除末尾元素:均摊 O(1)(自动扩容时可能需要复制数组)。
插入/删除中间元素:O(n)(需移动后续元素)。
查找元素:O(n)(需要遍历)。
注意事项
预分配容量:若已知元素数量,初始化时指定容量(如
new List<int>(100)
)可减少扩容开销。避免频繁中间操作:频繁插入或删除中间元素时,性能较差。
2. Dictionary<TKey, TValue>(哈希表)
字典,也称哈希表。就是存储着一对一对的值,和先前我们学习的普通哈希表差不多,只是可以自己定义类型
可以将Dictionary看作是一个键值对的集合,拥有泛型的HashTable
他也是基于键的哈希代码组织起来的键值对
键值对类型从HashTable的object变为了可以自己制定的泛型
特性
键值对存储:每个键唯一对应一个值。
无序集合:元素的顺序不固定(但遍历时顺序一致)。
基于哈希表实现:通过哈希函数快速定位键。
键不可重复:添加重复键会抛出异常。
申明一个字典出来:
注意:需要引用System.Collections.Generic
// 空字典
Dictionary<int, string> idToName = new Dictionary<int, string>();
Dictionary<string, float> productPrices = new Dictionary<string, float>();// 初始化时添加键值对
Dictionary<string, int> wordCounts = new Dictionary<string, int>
{{"apple", 5},{"banana", 3}
};
常用方法
添加/更新键值对:
dict.Add(key, value); // 添加(键必须唯一)
dict[key] = value; // 添加或覆盖
删除键值对:
注意:删除键值对,只用删除键即可
dict.Remove(key);
查找操作:
bool hasKey = dict.ContainsKey(key); // 检查键是否存在
bool hasValue = dict.ContainsValue(100);//检查是否值存在
bool success = dict.TryGetValue(key, out value); // 安全获取值
if (dict.TryGetValue("apple", out int count)) { /* ... */ }
遍历相关;
使用迭代器(遍历键值对):
foreach (KeyValuePair<string, int> pair in wordCounts)
{Console.WriteLine($"Key: {pair.Key}, Value: {pair.Value}");
}
遍历键或值:
这里当你不确定类型的时候,建议使用var代替,系统自动判别
foreach (string key in wordCounts.Keys)
{Console.WriteLine(key);
}foreach (int value in wordCounts.Values)
{Console.WriteLine(value);
}
不使用迭代器(通过集合转换):
var keys = wordCounts.Keys.ToList();
for (int i = 0; i < keys.Count; i++)
{string key = keys[i];Console.WriteLine(key);
}
性能
添加/删除/查找:平均 O(1)(哈希冲突时可能退化为 O(n))。
哈希函数质量:键的
GetHashCode()
应均匀分布以减少冲突。注意事项
键的不可变性:若键是可变对象(如自定义类),修改后会导致哈希值变化,无法定位原有值。
自定义键类型:需正确实现
GetHashCode()
和Equals()
方法。线程不安全:多线程操作需同步。
3. List vs Dictionary 对比
特性 | List<T> | Dictionary<TKey, TValue> |
---|---|---|
存储方式 | 有序元素集合 | 键值对(键唯一) |
查找性能 | O(n)(线性遍历) | O(1)(哈希查找) |
插入/删除性能 | 末尾操作:O(1);中间操作:O(n) | 平均 O(1) |
适用场景 | 需要顺序访问或索引操作 | 快速通过键查找值 |
4、总结
List<T> 的API及属性
类别 | 方法/属性 | 说明 | 返回值/示例 |
---|---|---|---|
声明与初始化 | List<T>() | 创建空列表 | List<int> list = new List<int>(); |
List<T>(int capacity) | 创建具有初始容量的列表 | List<string> list = new List<string>(100); | |
添加元素 | Add(T item) | 添加元素到列表末尾 | list.Add(10); |
Insert(int index, T item) | 在指定索引插入元素 | list.Insert(0, "A"); | |
删除元素 | Remove(T item) | 删除第一个匹配的元素 | list.Remove("apple"); |
RemoveAt(int index) | 删除指定索引的元素 | list.RemoveAt(0); | |
Clear() | 清空列表 | list.Clear(); | |
查找索引 | IndexOf(T item) | 返回第一个匹配项的索引(未找到返回-1) | int index = list.IndexOf(5); |
LastIndexOf(T item) | 返回最后一个匹配项的索引 | int lastIndex = list.LastIndexOf(5); | |
FindIndex(Predicate<T> match) | 返回第一个符合条件的元素的索引 | int index = list.FindIndex(x => x > 10); | |
FindLastIndex(Predicate<T> match) | 返回最后一个符合条件的元素的索引 | int lastIndex = list.FindLastIndex(...); | |
其他操作 | Sort() | 对列表排序(默认升序) | list.Sort(); |
BinarySearch(T item) | 二分查找(列表需已排序) | int pos = list.BinarySearch(5); | |
Exists(Predicate<T> match) | 检查是否存在符合条件的元素 | bool exists = list.Exists(x => x == 0); | |
属性 | Count | 列表中的元素数量 | int count = list.Count; |
Capacity | 列表的当前容量(可手动设置优化性能) | list.Capacity = 100; |
类别 | 方法/属性 | 说明 | 示例 |
---|---|---|---|
添加元素 | AddRange(IEnumerable<T> collection) | 批量添加集合元素到末尾 | list.AddRange(new[] { 4, 5, 6 }); |
插入元素 | InsertRange(int index, IEnumerable<T> collection) | 在指定位置插入集合元素 | list.InsertRange(0, new[] { -1, 0 }); |
删除元素 | RemoveRange(int index, int count) | 删除从索引 index 开始的 count 个元素 | list.RemoveRange(2, 3); |
获取子集 | GetRange(int index, int count) | 返回从索引 index 开始的 count 个元素的子列表(浅拷贝) | var sub = list.GetRange(1, 2); |
排序与反转 | Reverse(int index, int count) | 反转指定范围的元素顺序 | list.Reverse(0, 3); |
Dictionary<TKey, TValue> 的API及属性
类别 | 方法/属性 | 说明 | 返回值/示例 |
---|---|---|---|
声明与初始化 | Dictionary<TKey, TValue>() | 创建空字典 | Dictionary<int, string> dict = new ...; |
添加/更新 | Add(TKey key, TValue value) | 添加键值对(键必须唯一,否则抛出异常) | dict.Add(1, "A"); |
dict[key] = value | 添加或更新键对应的值 | dict[2] = "B"; | |
删除元素 | Remove(TKey key) | 删除指定键的键值对 | dict.Remove(1); |
Clear() | 清空字典 | dict.Clear(); | |
查找操作 | ContainsKey(TKey key) | 检查键是否存在 | bool exists = dict.ContainsKey(1); |
TryGetValue(TKey key, out TValue value) | 安全获取值(避免重复哈希计算) | if (dict.TryGetValue(1, out string val)) | |
ContainsValue(TValue value) | 检查值是否存在(时间复杂度O(n)) | bool hasVal = dict.ContainsValue("A"); | |
属性 | Keys | 获取所有键的集合 | foreach (var key in dict.Keys) { ... } |
Values | 获取所有值的集合 | foreach (var val in dict.Values) { ... } | |
Count | 字典中键值对的数量 | int count = dict.Count; | |
其他方法 | ElementAt(int index) (需Linq) | 按插入顺序获取指定位置的键值对(性能较差) | var pair = dict.ElementAt(0); |
差不多常用的就这些了,你如果还想继续深入了解,可以去看官方文档。