目录
前言
在C#开发中,合理使用数据结构能显著提升程序性能和代码可维护性。本文将带你探索四个经典的非泛型集合类:ArrayList
、Stack
、Queue
和Hashtable
,这些类型虽然已被泛型集合取代,但理解它们的设计思想仍是进阶学习的重要基石。
一、ArrayList(动态数组)
ArrayList是一个C#为我们封装好的类
本质是一个object类型的数组
ArrayList类帮助我们实现了很多方法
比如数组的增删查改
特点:
动态扩容:自动扩展容量(初始容量4,每次翻倍)
异构存储:可混合存储任意类型对象,就是里面啥都能存,存的是object基类对象
索引访问:支持类似数组的索引器操作
增删查改操作:
cs
ArrayList shoppingList = new ArrayList();
// ======== 增 ========
shoppingList.Add("牛奶"); // 追加元素到末尾
shoppingList.Add(3); // 支持不同类型存储
shoppingList.Insert(1, "面包"); // 在索引1位置插入元素
// ======== 查 ========
Console.WriteLine("第一个商品:" + shoppingList[0]); // 通过索引访问
Console.WriteLine("包含牛奶吗?" + shoppingList.Contains("牛奶")); // 元素存在性检查
// ======== 改 ========
shoppingList[2] = 5; // 通过索引直接修改元素
// ======== 删 ========
shoppingList.RemoveAt(0); // 删除指定索引元素
shoppingList.Remove(5); // 删除首个匹配元素
// 遍历集合
foreach (var item in shoppingList)
{
//这里迭代器遍历 别也可以使用普通的for循环遍历,因为前面说了,这其实就是一个很方便的数组
Console.WriteLine("剩余商品:" + item);
}
注意:需要引用命名空间using System.Collections;
你也可以直接将一整个数组加进去,不过加进去加的是数组里面的元素。
二、Stack(栈)
Stack是一个C#为我们封装好的类
本质也是一个object[]数组,只是封装了特4殊的存储规则Stack是队列存储容器
先进后出
核心方法:
-
核心方法:
-
Push()
:元素入栈 -
Pop()
:获取并移除栈顶元素 -
Peek()
:查看栈顶元素
-
代码示例:
你装入的是什么类型,弹出的就是什么类型。那么这种就必须你自己知道装入的是什么,非常不方便,后面我们会学习泛型,那个时候你就会发现没有泛型该多痛苦。
cs
Stack browserHistory = new Stack();
// ======== 增 ========
browserHistory.Push("首页"); // 元素压入栈顶
browserHistory.Push("商品页");
browserHistory.Push("购物车");
// ======== 查 ========
Console.WriteLine("当前页面:" + browserHistory.Peek()); // 查看栈顶元素(不移除)
// ======== 删 ========
var currentPage = browserHistory.Pop(); // 弹出并返回栈顶元素
Console.WriteLine("离开页面:" + currentPage);
/*
注意:栈结构不支持直接修改中间元素
所有操作只能在栈顶进行
*/
注意:
需要使用System.Collections命名空间
三、Queue(队列)
Quene是一个C#为我们封装好的类
本质也是一个object[]数组,只是封装了特殊的存储规则Quene是队列存储容器
先进先出
代码示例:
cs
Queue printerQueue = new Queue();
// ======== 增 ========
printerQueue.Enqueue("简历.pdf"); // 元素加入队尾
printerQueue.Enqueue("报告.doc");
printerQueue.Enqueue("照片.jpg");
// ======== 查 ========
Console.WriteLine("下一个文件:" + printerQueue.Peek()); // 查看队首元素(不移除)
// ======== 删 ========
while(printerQueue.Count > 0)
{
var file = printerQueue.Dequeue(); // 移除并返回队首元素
Console.WriteLine("正在打印:" + file);
}
/*
注意:队列结构不支持直接修改中间元素
遵循先进先出原则操作
*/
注意:
需要使用System.Collections命名空间
四、Hashtable(哈希表)
Hashtable又称散列表 是基于键的哈希代码组织起来的 键/值对集合
它的主要作用是提高数据查询的效率
使用键来访问集合中的元素
代码示例:
cs
Hashtable countryCodes = new Hashtable();
// ======== 增 ========
countryCodes.Add("CN", "中国"); // 添加键值对
countryCodes["US"] = "美国"; // 通过索引器添加
countryCodes["JP"] = "日本";
// ======== 查 ========
if(countryCodes.ContainsKey("CN")) // 检查键是否存在
{
Console.WriteLine("CN对应:" + countryCodes["CN"]); // 通过键查找值
}
// ======== 改 ========
countryCodes["US"] = "美利坚合众国"; // 通过索引器修改值
// ======== 删 ========
countryCodes.Remove("JP"); // 根据键删除条目
// 遍历键值对
foreach(DictionaryEntry entry in countryCodes)
{
Console.WriteLine($"键:{entry.Key},值:{entry.Value}");
}
注意:
需要引用命名空间:using System.Collections;
总结:
数据结构核心属性
数据结构 | 关键属性 | 说明 |
---|---|---|
ArrayList | Count |
当前实际存储的元素数量 |
Capacity |
当前分配的存储容量(当Count超过时自动扩容) | |
Stack | Count |
栈中元素个数 |
Queue | Count |
队列中元素个数 |
Hashtable | Count |
键值对数量 |
Keys |
包含所有键的ICollection集合 | |
Values |
包含所有值的ICollection集合 |
常见API汇总表格
数据结构 | 方法名 | 功能描述 |
---|---|---|
ArrayList | Add(object value) |
添加元素到列表末尾 |
Insert(int index, object value) |
在指定位置插入元素 | |
Remove(object obj) |
删除第一个匹配元素 | |
RemoveAt(int index) |
删除指定索引元素 | |
Contains(object obj) |
检查元素是否存在 | |
Stack | Push(object obj) |
元素压入栈顶 |
Pop() |
移除并返回栈顶元素 | |
Peek() |
查看栈顶元素(不移除) | |
Queue | Enqueue(object obj) |
元素加入队尾 |
Dequeue() |
移除并返回队首元素 | |
Peek() |
查看队首元素(不移除) | |
Hashtable | Add(object key, object value) |
添加键值对 |
Remove(object key) |
根据键删除条目 | |
ContainsKey(object key) |
检查键是否存在 | |
ContainsValue(object value) |
检查值是否存在 |
ArrayList与普通数组的本质区别
普通数组 是固定大小的连续内存空间,在声明时必须指定长度且不可更改。它支持快速随机访问,但缺乏动态调整能力,且只能存储单一类型元素(在C#中是类型安全的)。ArrayList本质上是一个动态包装的Object数组,通过以下特性突破普通数组的限制:
-
动态扩容:当元素数量超过当前容量时,自动创建新数组并复制元素(默认扩容策略:容量翻倍)
-
类型混合:由于存储Object类型元素,可以混合存放不同数据类型
-
丰富操作:提供插入、删除、搜索等内置方法,无需手动实现
-
容量管理 :通过
Capacity
属性显式控制底层数组大小,平衡性能与内存
代价是:
-
值类型存储时会产生装箱拆箱开销
-
读取元素时需要强制类型转换
-
自动扩容时存在内存复制消耗