别人没那么重要,我也没那么重要,好好活着,把能做的小事做好,够不到的东西就放弃,承认就好。做一个心情好能睡着的人,你所有事情都会在正轨上。

【C#】非泛型集合

1. 简介

  在C#中,非泛型集合指的是那些在.NET Framework早期版本中定义的集合类,它们不使用泛型,因此可以存储任何类型的对象。这些集合类位于System.Collections命名空间中。由于它们不使用泛型,所以当你从这些集合中读取数据时,可能需要进行类型转换。

  以下将会介绍非泛型集合的一些基本用法。请注意,以下遍历只是为了测试是否有元素。

2. ArrayList

  在C#中,ArrayList 是一个非泛型集合,属于 System.Collections 命名空间。它是一个动态数组,可以存储任何类型的对象。

  由于 ArrayList 是非泛型的,这意味着它在运行时而非编译时进行类型检查,因此在使用时可能需要进行类型转换。

2.1. 创建 ArrayList 实例

1 ArrayList list = new ArrayList();

2.2. 向 ArrayList 添加元素

1 // 使用 Add 方法
2 list.Add("Hello");
3 list.Add(123);
4 list.Add(DateTime.Now);
5 
6 // 使用 AddRange 方法添加多个元素
7 ArrayList moreItems = new ArrayList { "World", 456, DateTime.UtcNow };
8 list.AddRange(moreItems);

2.3. 访问 ArrayList 中的元素

1 object firstItem = list[0]; // 使用索引访问,索引从0开始

2.4. 遍历 ArrayList

1 foreach (object item in list)
2 {
3     Console.WriteLine(item);
4 }

2.5. 移除 ArrayList 中的元素

1 // 按值移除
2 bool removed = list.Remove("Hello");
3 
4 // 按索引移除
5 list.RemoveAt(0);

2.6. ArrayList 的搜索和排序

1 // 查找特定项的索引
2 int index = list.IndexOf(123);
3 
4 // 排序
5 list.Sort();

2.7. ArrayList 的容量和截断

1 // 获取列表中的项目数
2 int count = list.Count;
3 
4 // 清空列表
5 list.Clear();
6 
7 // 设置列表的容量
8 list.Capacity = 100;

2.8. 注意

  • 使用 ArrayList 时,由于它存储的是 object 类型,所以当你从列表中取出元素时,可能需要进行显式类型转换。
  • 泛型集合类(如 List<T>)在类型安全性、性能和代码可读性方面都优于 ArrayList。因此,除非有特定的向后兼容性要求,否则推荐使用泛型集合类。
  • ArrayList 不是线程安全的。如果你需要在多线程环境中使用它,需要自行实现同步。

3. Hashtable

  在C#中,Hashtable 是一个基于哈希表的键值对集合,实现了 IDictionary 接口,属于 System.Collections 命名空间。

  由于 Hashtable 是非泛型的,它在存储键和值时不需要指定类型,因此可以存储任何类型的键和值,但这也意味着失去了类型安全的好处。

3.1. 创建 Hashtable 实例

1 Hashtable hashtable = new Hashtable();

3.2. 向 Hashtable 添加键值对

1 // 使用 Add 方法
2 hashtable.Add("key1", "value1");
3 hashtable.Add(123, "number value");
4 
5 // 直接使用索引器
6 hashtable["key2"] = "value2";

3.3. 访问 Hashtable 中的元素

1 // 使用键获取值
2 object value1 = hashtable["key1"];
3 
4 // 使用索引器获取值,键不存在时返回 null
5 object value2 = hashtable["key3"] ?? "default value";

3.4. 遍历 Hashtable

1 foreach (DictionaryEntry entry in hashtable)
2 {
3     Console.WriteLine("Key: " + entry.Key + ", Value: " + entry.Value);
4 }

对于C# 2.0及以后版本,可以使用更现代的LINQ查询语法:

1 foreach (var entry in hashtable)
2 {
3     Console.WriteLine("Key: " + entry.Key + ", Value: " + entry.Value);
4 }

3.5. 移除 Hashtable 中的元素

1 // 使用 Remove 方法
2 hashtable.Remove("key1");
3 
4 // 使用索引器移除(如果键不存在,则不抛出异常)
5 hashtable["key2"] = null; // 这将从 Hashtable 中移除 "key2"

3.6. Hashtable 的键值检查

1 // 检查键是否存在
2 bool containsKey = hashtable.ContainsKey(123);
3 
4 // 检查值是否存在
5 bool containsValue = hashtable.Contains("value1");

3.7. Hashtable 的同步

  Hashtable 提供了一个静态方法 Synchronized,可以返回一个线程安全的 Hashtable 包装器。

1 Hashtable syncHashtable = Hashtable.Synchronized(hashtable) as Hashtable;

3.8. 注意

  • Hashtable 是线程安全的,但性能可能不如 Dictionary<TKey, TValue>,后者是泛型字典实现,通常推荐在需要键值对集合时使用。
  • Hashtable 使用键的 GetHashCode 方法和 Equals 方法来计算索引和比较键,因此确保所有键都适当地实现了这些方法。
  • 由于 Hashtable 是非泛型的,当你取出值时,可能需要进行类型转换。
  • 尽管 Hashtable 在处理多种不同类型键值对时很有用,但在编写新的C#代码时,通常建议使用泛型集合,如 Dictionary<TKey, TValue>,因为它们提供了更好的类型安全性和性能。

4. Queue

  在C#中,Queue 是一个基于集合的类,它实现了先进先出(FIFO)的数据结构,属于 System.Collections.Generic 命名空间。这意味着第一个添加到队列中的元素将是第一个被移除的元素。

4.1. 创建 Queue<T> 实例

1 Queue<int> numberQueue = new Queue<int>();

4.2. 向 Queue<T> 添加元素

1 // 使用 Enqueue 方法在队列尾部添加元素
2 numberQueue.Enqueue(1);
3 numberQueue.Enqueue(2);
4 numberQueue.Enqueue(3);

4.3. 访问 Queue<T> 中的元素

1 // 查看队列头部的元素但不移除
2 int frontItem = numberQueue.Peek(); // 返回 1
3 
4 // 访问队列中的元素(通过索引)
5 int itemAtIndex = numberQueue[0]; // 同样返回 1

4.4. 遍历 Queue<T>

1 foreach (int item in numberQueue)
2 {
3     Console.WriteLine(item);
4 }

4.5. 移除 Queue<T> 中的元素

1 // 使用 Dequeue 方法移除队列头部的元素
2 int dequeuedItem = numberQueue.Dequeue(); // 返回并移除 1

4.6. 查看 Queue<T> 中的元素数量

1 int count = numberQueue.Count; // 返回当前队列中的元素数量

4.7. 清空 Queue<T>

1 numberQueue.Clear(); // 移除队列中的所有元素

4.8. 注意

  • 队列的头部由 PeekDequeue 方法操作,而队列的尾部由 Enqueue 方法操作。
  • 尝试访问队列中不存在的元素(例如,队列为空时调用 DequeuePeek)将抛出 InvalidOperationException 异常。
  • 队列是后进先出的集合,这使得它们非常适合用于跟踪按特定顺序处理的元素。
  • 在多线程环境中,如果需要线程安全的队列操作,可以使用 System.Collections.Concurrent 命名空间下的 ConcurrentQueue<T> 类。这个类是线程安全的,并且设计用于高并发场景。
  • Queue<T> 是处理需要保持添加顺序的数据集合时的理想选择,例如任务调度、事件处理队列等。

5. Stack

  在C#中,Stack<T> 是一个后进先出(LIFO)的集合类,属于 System.Collections.Generic 命名空间。

  这意味着最后添加到栈中的元素将是第一个被移除的元素。

5.1. 创建 Stack<T> 实例

1 Stack<int> numberStack = new Stack<int>();

5.2. 向 Stack<T> 添加元素

1 // 使用 Push 方法在栈顶添加元素
2 numberStack.Push(1);
3 numberStack.Push(2);
4 numberStack.Push(3);

5.3. 访问 Stack<T> 中的元素

1 // 查看栈顶的元素但不移除
2 int topItem = numberStack.Peek(); // 返回 3

5.4. 遍历 Stack<T>

1 foreach (int item in numberStack)
2 {
3     Console.WriteLine(item); // 输出顺序会是 3, 2, 1
4 }

5.5. 移除 Stack<T> 中的元素

1 // 使用 Pop 方法移除栈顶的元素
2 int poppedItem = numberStack.Pop(); // 返回并移除 3

5.6. 查看 Stack<T> 中的元素数量

1 int count = numberStack.Count; // 返回当前栈中的元素数量

5.7. 清空 Stack<T>

1 numberStack.Clear(); // 移除栈中的所有元素

5.8. 注意

  • 栈的顶部由 PeekPop 方法操作,而 Push 方法用于在栈顶添加元素。
  • 尝试 PopPeek 一个空栈将抛出 InvalidOperationException异常。
  • 栈常用于算法和数据结构中,如表达式求值、回溯算法、撤销操作的实现等。
  • 在多线程环境中,如果需要线程安全的栈操作,可以使用 System.Collections.Concurrent 命名空间下的 ConcurrentStack<T> 类。这个类是线程安全的,并且设计用于高并发场景。
  • Stack<T> 是处理需要保持添加顺序的逆序数据集合时的理想选择。与 List<T>Queue<T> 相比,Stack<T> 提供了更明确的添加和移除操作,即仅在集合的一端进行操作。

6. SortedList

  在C#中,SortedList 是一个非泛型集合类,它存储键值对并自动按照键对项进行排序。

  SortedList 类属于 System.Collections 命名空间,并且不是类型安全的,因为它的键和值都是以 object 类型存储。

6.1. 创建 SortedList 实例

1 SortedList sortedList = new SortedList();

6.2. 向 SortedList 添加键值对

1 // 使用 Add 方法添加键值对
2 sortedList.Add("key1", "value1");
3 sortedList.Add("key2", "value2");

6.3. 使用索引器添加键值对

1 // 使用索引器添加或更新键值对
2 sortedList["key3"] = "value3";

6.4. 访问 SortedList 中的元素

1 // 使用键获取值
2 object value = sortedList["key1"];

6.5. 遍历 SortedList

1 // 遍历键值对
2 foreach (DictionaryEntry kvp in sortedList)
3 {
4     Console.WriteLine("Key: " + kvp.Key + ", Value: " + kvp.Value);
5 }

对于C# 2.0及以后版本,可以使用更现代的LINQ查询语法:

1 foreach (var kvp in sortedList)
2 {
3     Console.WriteLine("Key: " + kvp.Key + ", Value: " + kvp.Value);
4 }

6.6. 移除 SortedList 中的元素

1 // 使用 Remove 方法移除键值对
2 sortedList.Remove("key1");

6.7. SortedList 的键值检查

1 // 检查键是否存在
2 bool containsKey = sortedList.ContainsKey("key2");
3 
4 // 检查值是否存在
5 bool containsValue = sortedList.ContainsValue("value2");

6.8. SortedList 的键排序

  由于 SortedList 在添加键值对时会自动按照键排序,遍历 SortedList 时,元素会按照键的排序顺序出现。

6.9. 注意

  • 由于 SortedList 是非泛型的,当你从列表中取出值时,可能需要进行类型转换。
  • 泛型集合类(如 Dictionary<TKey, TValue> SortedDictionary<TKey, TValue>)在类型安全性、性能和代码可读性方面都优于 SortedList。因此,除非有特定的向后兼容性要求,否则推荐使用泛型集合类。
  • SortedList 适合于需要保持键值对顺序的场景,例如,当你需要快速查找、插入和删除操作,并且需要元素保持有序时。
  • 在编写新的C#代码时,通常建议使用泛型集合,因为它们提供了更好的类型安全性和性能。如果需要一个有序的键值对集合,可以使用 SortedDictionary<TKey, TValue>

7. 总结

  • 非泛型集合类在处理类型时需要进行类型转换,这可能会导致运行时错误,因此在使用时需要格外小心。
  • 泛型集合类在类型安全性、性能和代码可读性方面都优于非泛型集合类。因此,除非有特定的向后兼容性要求,否则推荐使用泛型集合类。
  • 由于非泛型集合可以存储任何类型的对象,它们在编译时不会提供类型检查,这可能会隐藏错误直到运行时。
  • 尽管非泛型集合在.NET Framework中仍然可用,但在编写新的C#代码时,通常建议使用泛型集合,因为它们提供了更好的类型安全性和性能。

 

时间:2024年4月29日

 

posted @ 2024-04-29 09:42  一路狂奔的乌龟  阅读(98)  评论(0)    收藏  举报
返回顶部