【一文了解】C#基础-集合

目录

集合

1.集合分类

1.1.非泛型集合

1.2.泛型集合

1)列表(List)

2)字典(Dictionary)

3)队列(Queue)

4)栈(Stack)

5)哈希集合(HashSet)

2.集合的常见操作

3.区分泛型集合与非泛型集合

3.1.非泛型集合

1)优点

2)缺点

3.2.泛型集合

1)优点

2)缺点

总结


本篇文章来学习一下集合,C#集合主要分为非泛型集合与泛型集合。

集合

集合(Collection)是一种用于存储、管理和操作一组数据的容器或数据结构。在编程中,集合通常指的是能够存储多个元素的数据结构,它允许对元素进行各种操作,比如添加、删除、查找、排序、遍历等。集合可以是有序的,也可以是无序的,元素的类型可以是基本数据类型,也可以是对象。

集合是一种数据结构,一种数据容器。集合大小可变,空间不一定连续;分为非泛型集合(ArrayList)、泛型集合(列表(List)、字典 (Dictionary)、栈 (Stack) 、队列 (Queue)、哈希集合(HashSet))。

1.集合分类

1.1.非泛型集合

非泛型集合使用object类型来存储所有元素,可以将任何类型的对象放入集合中,但在取出元素时需要进行强制类型转换,这可能导致InvalidCastException等运行时错误,有时候需要手动转换类型。它们存储的元素通常是object类型,因此可以存储任何类型的数据。常见的非泛型集合包括ArrayList、Hashtable、Queue、Stack等。使用命名空间System.Collections。

特点:由于存储的元素是object类型,可能需要类型转换,运行时性能较差。

cs 复制代码
using System.Collections;
using UnityEngine;

/// <summary>
/// ArrayList
/// </summary>
public class ArrayListTest
{
    private ArrayList list = new ArrayList();//非泛型集合
    private GameObject cube;

    private void Start()
    {
        list.Add(1);
        list.Add("张三");
        list.Add("李四");
        list.Add(cube);
        //判断集合中是否含有"张三",并将"张三"移除
        if (list.Contains("张三"))
            list.Remove("张三");
        //从索引1位置插入"王五"元素
        list.Insert(1, "王五");
        //从索引0位置移除2个元素
        list.RemoveRange(0, 2);
        for (int i = 0; i < list.Count; i++)
        {
            Debug.Log(list[i]);//有序输出
        }
    }
}

1.2.泛型集合

泛型集合是通过使用泛型类型参数T来提供类型安全的集合类型。常见的泛型集合包括List<T>、Dictionary<TKey, TValue>、Queue<T>、Stack<T>、HashSet<T>等。使用命名空间System.Collections.Generic。

1)列表(List)

List<T>是C#中最常用的集合类型之一,它是一个动态数组,允许在运行时添加、删除、修改元素,大小是可变的。

特点:动态大小,可以在运行时动态增减元素;支持随机访问,可以通过索引访问列表中的元素。

cs 复制代码
using System.Collections.Generic;
using UnityEngine;
  
/// <summary>
/// List<T>
/// </summary>  
public class ListTest
{
    private List<string> strList = new List<string>();//泛型集合(需指定类型)
    private List<int> intList = new List<int>() { 1, 10, 5, 100 };

    private void Start()
    {
        strList.Add("张三");
        strList.Add("李四");
        strList.Add("王五");
        strList.Add("赵六");
        strList.Remove("李四");
        foreach (var item in strList)
        {
            Debug.Log(item + " ");
        }

        intList.Sort();
        foreach (var item in intList)
        {
            Debug.Log(item + " ");
        }
    }
}
2)字典(Dictionary)

Dictionary<TKey, TValue>是一个基于键-值对的集合,存储的每个元素都有一个唯一的键与对应的值。

特点:键值对,每个元素都有一个唯一的键,值是与键相对应的数据;快速查找,通过键可以快速查找到对应的值。

cs 复制代码
using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 字典(字典嵌套字典) Dictionary<TKey,TValue>
/// </summary>
public class DicTest : MonoBehaviour
{
    //主键   子键   值
    private Dictionary<string, Dictionary<string, string>> myDic
        = new Dictionary<string, Dictionary<string, string>>();
    
    private void Start()
    {
        //姓名 QQ 地址
        myDic.Add("张三", new Dictionary<string, string>());
        myDic["张三"].Add("QQ", "12345");
        myDic["张三"].Add("地址", "北京");
        print(myDic["张三"]["地址"]);//获取值
    }
}
3)队列(Queue)

Queue<T>是一个先进先出(FIFO,First In First Out)的集合。它常用于实现排队的场景,比如任务调度、打印队列等。

特点:先进先出,队列中的元素按照它们被添加的顺序处理;添加操作(Enqueue)向队列的末尾添加元素,移除操作(Dequeue)从队列的前端移除元素。

cs 复制代码
using UnityEngine;
using System.Collections;

/// <summary>
/// 队列Queue (先进先出) Queue<T> 
/// </summary>
public class QueueTest : MonoBehaviour
{
    private Queue queue = new Queue();
    
    private void Start()
    {
        queue.Enqueue("入队");
        print(queue.Dequeue());//移除并读队首
        print(queue.Peek());//读取队首但不移除
        print(queue.Count);
    }
}
4)栈(Stack)

Stack<T> 是一个后进先出(LIFO,Last In First Out)的集合。它用于管理临时数据,如递归操作、回退功能等。

特点:后进先出,栈中的元素按照它们被添加的顺序的相反顺序被处理;添加操作(Push)将元素压入栈顶,移除操作(Pop)将栈顶元素弹出。

cs 复制代码
using UnityEngine;
using System.Collections;

/// <summary>
/// 栈Stack (后进先出)  Stack<T>
/// </summary>
public class StackTest : MonoBehaviour
{
    /*
     *主菜单->选项->游戏选项->难度调节->简单
     *逐层压栈,逐层弹栈,最后一个(主菜单),只读不弹
     */
    private Stack stack = new Stack();
    
    private void Start()
    {
        //逐层压栈
        stack.Push("主菜单");
        stack.Push("选项");
        stack.Push("游戏选项");
        stack.Push("难度调节");
        stack.Push("简单");
        //逐层弹栈,读取用while
        while (stack.Count > 1)
        {
            print("弹栈 : " + stack.Pop());
        }
        print("只读不弹 : " + stack.Peek());
    }
}
5)哈希集合(HashSet)

HashSet<T> 是一个无序的集合,它存储唯一的元素,不允许有重复元素。

特点:无序,元素的顺序不是固定的;唯一性,不会允许添加重复的元素。

cs 复制代码
HashSet<int> uniqueNumbers = new HashSet<int> { 1, 2, 3, 4, 4 };//4会被去重

2.集合的常见操作

1)添加元素:大多数集合类型都提供Add或类似的方法来添加元素。

2)删除元素:可以通过Remove、RemoveAt或Dequeue等方法来删除元素。

3)查找元素:可以使用Contains、IndexOf或通过键在字典中查找元素。

4)遍历元素:大多数集合都支持通过foreach或索引来遍历其中的元素。

5)清空集合:有些集合提供了Clear方法,用于清空集合中的所有元素。

3.区分泛型集合与非泛型集合

3.1.非泛型集合

1)优点

①灵活性高:非泛型集合可以存储不同类型的数据,因为它们是基于object类型的。例如,一个ArrayList可以存储 int、string、double等不同类型的对象,对于动态数据类型处理很有用。

cs 复制代码
ArrayList list = new ArrayList();
list.Add(1);//添加int类型
list.Add("hello");//添加string类型
list.Add(3.14);//添加double类型

②向后兼容:非泛型集合是C#早期版本的标准,因此在一些老旧的代码库或库中,非泛型集合仍然被广泛使用。如果需要与旧版代码兼容,非泛型集合可能更方便。

③动态类型支持:非泛型集合对不同类型的数据支持更加灵活,适合动态类型的需求。例如,反射和序列化场景中,可能不清楚对象的确切类型时,使用object类型的集合会更方便。

2)缺点

①类型安全差:由于集合元素存储为object类型,因此无法保证集合中元素的类型一致性,所以必须在操作集合时进行类型检查和强制类型转换,以避免在运行时发生错误。

cs 复制代码
ArrayList list = new ArrayList();
list.Add(1);//存储int类型
list.Add("hello");//存储string类型

//强制类型转换可能导致错误
int number = (int)list[1];//运行时错误:无法将string转换为int

②性能较差:由于元素存储为object类型,在访问时需要进行装箱(对于值类型)或类型转换。这些操作增加了运行时的开销,尤其是在处理大量数据时,性能会有所下降。

③无法直接使用LINQ:非泛型集合不直接支持LINQ查询。虽然可以将其转换为泛型集合来使用LINQ,但这样会增加额外的开销。

④维护难度大:如果集合中存储的元素类型不同,代码的可读性和维护性会变差。因此必须记住集合中包含哪些类型,避免发生类型转换错误。

3.2.泛型集合

1)优点

①类型安全:泛型集合允许在编译时指定元素类型,保证集合中的元素类型一致。这避免了运行时类型转换错误,提高了代码的安全性。如,List<int>只能存储int类型的元素,不会出现类型不匹配的错误。

cs 复制代码
List<int> numbers = new List<int>();
numbers.Add(1);
numbers.Add(2);
//numbers.Add("string");//编译时错误,不能将string添加到List<int>中

②性能优越:由于泛型集合在编译时确定元素类型,不需要进行类型转换或装箱操作,因此性能通常更好。对于值类型(如 int、struct等),没有装箱过程,因此可以避免额外的内存开销。

③可读性和维护性:泛型集合提高了代码的可读性和可维护性,因为明确知道集合中的元素类型,代码更加直观。

④支持LINQ查询:泛型集合与LINQ查询集成得很好,能够直接进行类型安全的查询和操作。

2)缺点

①类型限制:泛型集合要求指定集合元素的类型,这可能导致在一些动态类型的场景中,使用泛型集合不太方便。如在处理不同类型的对象时可能需要使用object类型(这就绕回到非泛型集合的做法)。

②开发复杂度:如果需要存储不同类型的数据,可以通过object类型的泛型集合来实现,但这可能导致类型转换的麻烦,降低了类型安全。

总结

1)泛型集合:当需要高性能、类型安全、并且处理明确类型的数据时,应该选择泛型集合。

2)非泛型集合:当需要处理不同类型的数据(如存储不同类型的对象)或者与旧版代码兼容时,选择非泛型集合。

|-------------|---------------------|----------------------|
| 特性 | 泛型集合 | 非泛型集合 |
| 类型安全 | 强制指定元素类型,编译时类型检查 | 使用Object类型,缺乏类型安全 |
| 性能 | 更高,避免装箱和类型转换 | 较低,需要装箱和类型转换 |
| 灵活性 | 只能存储指定类型的元素 | 可以存储任何类型的元素 |
| 开发复杂度 | 需要指定元素类型,适用于类型明确的场景 | 可存储不同类型的数据,适用于动态类型场景 |
| 兼容性 | 现代C#项目,广泛使用 | 向后兼容旧版C#代码,支持更动态的需求 |
| 支持 LINQ | 支持LINQ查询 | 不直接支持LINQ查询 |

泛型集合的类型更安全、性能更高,在C#的大多数开发中,更推荐使用泛型集合。而非泛型集合由于灵活性更高,适合存储多种类型的数据,但因类型安全差,性能较低,需谨慎使用。

好了,今天的分享到这里就结束啦,希望对你有所帮助~

相关推荐
也无晴也无风雨27 分钟前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
皮皮陶1 小时前
Unity WebGL交互通信
unity·交互·webgl
程序员正茂1 小时前
PICO+Unity MR空间网格
unity·mr·pico
程序员正茂1 小时前
PICO+Unity MR空间锚点
unity·pico·空间锚点
SRY122404192 小时前
javaSE面试题
java·开发语言·面试
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架