C#数据结构——List

本文详细介绍了C#中的List数据结构,包括基本概念、Count和Capacity属性、不同构造方法、常用API如Add、Remove和Clear,以及特别提醒,强调了List在存储值类型和引用类型时的行为差异和性能考虑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

基本概念

基本属性

Count

Capacity

构造List

无参构造

一参构造(int)

一参构造 (Array)

拷贝构造

其他

常用API

Add(T)

AddRange(Predicate)

Remove()

Clear()

其他API

特别提醒


基本概念

        List类是ArrayList类的泛型版本,相较于自由的ArrayList,List需要在定义时提前指定其内部元素的数据类型,但相对的,List相较于ArrayList在使用其内部元素时也不需要进行装箱、拆箱等操作,在存储元素为值类型的情况下List会有更为优秀的性能表现。

        与ArrayList相同,当使用List.Add()向List当中置入数据时,若List空间不足,则List会自动扩容,因此使用List不必担心像数组一样出现空间不足的情况。

        但与数组相似的是,当使用索引对List元素进行访问时,即:List[index],可能会像数组一样出现越界问题,而List的范围则是[0,List.Count),即访问索引必须大于等于0并小于List.Count。

基本属性

Count

       上文说到,List的范围是[0,Count),即访问索引必须大于等于0并小于Count,因此Count也可视作List内部元素的数量

Capacity

        List.Capacity是List的实际容量,这个容量对于同一个List只会扩张不会缩小,当使用了List.Remove()等操作时只会缩小Count,对Capacity不会产生影响;这意味着对于同一个List对象,即便是移除了内部元素,List的实际占用空间也不会缩小,但如果List内部元素是引用类型,则会删除其引用。

        同时,如果引发了List的扩容,则Capacity会扩张到原来的两倍,但并不是直接扩张原来的空间大小,而是申请一片新的内存空间,并将原有元素拷贝到新空间当中,因此扩容因造成一定的性能消耗。

构造List

无参构造

        这是最基本的List构造方法,但大多数情况下推荐使用其他方法优化性能

List<int> _list_None = new List<int>();

一参构造(int)

        这个构造函数事实上是用于指定List生成后的容量,即List.Capacity,在已知元素大致数量的情况下,使用该构造函数可以减少List的扩容,从而减少性能损耗。

List<int> _list_Capaacity = new List<int>(8);

一参构造 (Array)

        List也可以使用元素类型相同的一维数组进行构造,用这种方法生成的List其Count与Capacity都取决于传入的数组大小

int[] _array = new[] { 0, 1, 2 };
List<int> _list_Array = new List<int>(_array);

拷贝构造

        List也可以使用其他List对象进行构造,使用这种方法生成的List其Count与Capacity都取决于拷贝的List对象的Count

List<int> _list_List = new List<int>(8);
//生成后实际Count和Capacity都为0,因为其拷贝的List是空的
List<int> _list_OtherList = new List<int>(_list_List);

其他

        List其实也可以使用其他集合进行构造,比如:

Dictionary<int, int> _dictionary = new Dictionary<int, int>(4);
List<int> _lsit_Key = new List<int>(_dictionary.Keys);
List<int> _lsit_Value = new List<int>(_dictionary.Values);

        又或者:

List<int> _list_Element = new List<int>()
{
    0, 1, 2, 3, 4, 5
};

常用API

Add(T)

        List.Add()用于向List中添加元素,新添加的元素会存在于List尾部,当List容量不足时会引起扩容。

List<int> _list = new List<int>(2);
_list.Add(0);// Capacity:2 ,内部元素:0
_list.Add(1);// Capacity:2 ,内部元素:0, 1

//扩容
_list.Add(2);// Capacity:4 ,内部元素:0, 1, 2
_list.Add(3);// Capacity:4 ,内部元素:0, 1, 2, 3

AddRange(Predicate<T>)

        List.AddRange()用于将其他集合中的内容拷贝到当前List的尾部

List<int> _list = new List<int>(6)
{
    0, 1, 2, 3, 2, 3
};
List<int> _list_AddRange = new List<int>()
{
    9, 8, 7
};
_list_AddRange.AddRange(_list);// 内部元素为:9, 8, 7, 0, 1, 2, 3, 2, 3

Remove()

        List.Remove()用于移除指定元素,但只会移除第一个匹配项,未找到匹配项时,该函数不会有额外操作。

List<int> _list = new List<int>(6)
{
    0, 1, 2, 3, 2, 3
};
_list.Remove(4);//元素:0, 1, 2, 3, 2, 3,未找到则不生效
_list.Remove(2);//元素:0, 1, 3, 2, 3 

Clear()

        List.Clear()用于清除List当中的全部元素

List<int> _list = new List<int>(6)
{
    0, 1, 2, 3, 2, 3
};
_list.Clear();//什么都没了,Count == 0

其他API

  • BinarySearch(T):搜索整个已排序的 List<T> 中的元素,并返回指定对象的从零开始的索引。
  • Contains(T):确定某元素是否在List<T>中。
  • ConvertAll<TOutput>(Converter<T,TOutput>):将 List<T> 中的所有元素转换为指定类型,并返回包含转换后元素的数组。
  • CopyTo(T[], Int32):从目标数组的指定索引处开始,将整个 List<T> 复制到兼容的一维数组中。
  • Exists(Predicate<T>):确定 List<T> 中是否存在与指定谓词定义的条件匹配的元素。
  • Find(Predicate<T>):检索与指定谓词定义的条件匹配的第一个元素。
  • FindAll(Predicate<T>):检索与指定谓词定义的条件匹配的所有元素。
  • FindIndex(Predicate<T>):检索与指定谓词定义的条件匹配的第一个元素的从零开始的索引。
  • FindLast(Predicate<T>):检索与指定谓词定义的条件匹配的最后一个元素。
  • FindLastIndex(Predicate<T>):检索与指定谓词定义的条件匹配的最后一个元素的从零开始的索引。
  • ForEach(Action<T>):对 List<T> 中的每个元素执行指定操作。
  • GetEnumerator():返回循环访问 List<T> 的枚举器。
  • GetRange(Int32, Int32):返回一个新 List<T>,它包含源 List<T> 中某个范围内的元素。
  • IndexOf(T):搜索指定对象,并返回整个 List<T> 中第一个匹配项从零开始的索引。
  • Insert(Int32, T):在 List<T> 的指定索引处插入一个元素。
  • InsertRange(Int32, IEnumerable<T>):在 List<T> 的指定索引处插入集合中所有元素。
  • LastIndexOf(T):搜索指定对象,并返回整个 List<T> 中最后一个匹配项从零开始的索引。
  • RemoveAll(Predicate<T>):从 List<T> 中删除与指定谓词定义的条件匹配的所有元素。
  • RemoveAt(Int32):删除 List<T> 的指定索引处的元素。
  • RemoveRange(Int32, Int32):从 List<T> 中移除某个范围内的所有元素。
  • Reverse():反转整个 List<T> 中所有元素顺序。
  • Sort():使用默认比较器对整个 List<T> 中所有元素进行排序。

特别提醒

  • 如果List内部的元素是值类型的复合数据类型,那不要试图通过索引直接修改该复合数据类型的内部成员,因为List返回的对象事实上是该数据的拷贝,但如果是引用类型,则返回的是该引用类型则返回的是引用:
class MainClass
{
    struct MyStruct
    {
        public int data;

        public MyStruct(int data)
        {
            this.data = data;
        }
    }
    

    static void Main(string[] args)
    {
        List<MyStruct> _list_Struct = new List<MyStruct>() { new MyStruct(5) };
        _list_Struct[0].data = 10;//报错
    }
}

  • 因为List是引用类型,所以如果使用了"="将一个List对象赋值给另一个List对象,那对这两个对象进行操作本质上都是操作同一份数据
    List<int> _listA = new List<int>() { 0, 1, 2, 3 };
    List<int> _listB = _listA;
    _listB[0] = 3;//此时会发现 _listA[0] 也变成了3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值