当前位置: 首页 > news >正文

【C#知识点详解】List<T>储存结构详解

        今天来介绍一下List内部的存储结构,话不多说直接开始。

内部数据

        List内部采用了连续数组的方式存储数据,其中包含了三个重要的成员变量,示例如下:

// 用于存储数据的数组
internal T[] _items;
// 列表中实际包含数据的数量
internal int _size;
// 版本号,用于迭代时检测是否修改
internal int _version;
  • T[] _items:存储数据的数组,实际存储数据的地方。
  • int _size:实际包含数据的数量,用于记录_items实际存储数据的数量。
  • int _version:版本号,用于检测List迭代时_items是否被修改。

        

数据操作

        添加数据

        List在添加数据时是在尾部添加,添加时会对_size数量增加,示例代码如下:

public void Add(T item)
{
    _version++;
    T[] array = _items;
    int size = _size;
    if ((uint)size < (uint)array.Length)
    {
        _size = size + 1;
        array[size] = item;
    }
}

        当_items容积不够时,List会进行扩容,会先计算扩容后容积的大小,默认会扩容到原来的2倍,然后会创建一个新的newItems数组,并将原先的_items数据拷贝进去,示例代码如下:

internal void Grow(int capacity)
{
    int newCapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length;

    if ((uint)newCapacity > Array.MaxLength) 
		newCapacity = Array.MaxLength;

    if (newCapacity < capacity) 
		newCapacity = capacity;

    Capacity = newCapacity;
}

public int Capacity
{
    get => _items.Length;
    set
    {
        if (value != _items.Length)
        {
            if (value > 0)
            {
                T[] newItems = new T[value];
                if (_size > 0)
                {
                    Array.Copy(_items, newItems, _size);
                }
                _items = newItems;
            }
        }
    }
}

        

        移除数据

        List在移除数据的时候较常用的两个方法是Remove()RemoveAt()Remove()的执行过程也是先找到数据所在位置的索引,然后在调用RemoveAt()方法,示例代码如下:

public bool Remove(T item)
{
    int index = IndexOf(item);
    if (index >= 0)
    {
        RemoveAt(index);
        return true;
    }

    return false;
}

        RemoveAt()的执行过程是将传入索引位置之后的数据拷贝前移,数据拷贝后再将最后一位设置成默认值,示例代码如下:

public void RemoveAt(int index)
{
    _size--;
    if (index < _size)
    {
        Array.Copy(_items, index + 1, _items, index, _size - index);
    }
    if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
    {
        _items[_size] = default!;
    }
    _version++;
}

        

        插入数据

        List在插入数据时会调用Insert(int index, T item)接口,插入数据的执行过程与移除数据比较相似,示例代码如下:

public void Insert(int index, T item)
{
    // Note that insertions at the end are legal.
    if ((uint)index > (uint)_size)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
    }
    if (_size == _items.Length) Grow(_size + 1);
    if (index < _size)
    {
        Array.Copy(_items, index, _items, index + 1, _size - index);
    }
    _items[index] = item;
    _size++;
    _version++;
}

        

        访问数据

        在List中访问数据最常用的方式是索引访问,通过this[int index]直接获取修改数据。由于数据存储在_items数组中,访问数据时唯一需要注意的只有越界问题。示例代码如下:

public T this[int index]
{
    get
    {
        // Following trick can reduce the range check by one
        if ((uint)index >= (uint)_size)
        {
            ThrowHelper.ThrowArgumentOutOfRange_IndexMustBeLessException();
        }
        return _items[index];
    }

    set
    {
        if ((uint)index >= (uint)_size)
        {
            ThrowHelper.ThrowArgumentOutOfRange_IndexMustBeLessException();
        }
        _items[index] = value;
        _version++;
    }
}

使用场景

        基于前面对List的介绍,List的使用可以归纳如下:

  • List提供了灵活的数组存储空间,当数组元素数量不确定时可以选择List。
  • List提供了灵活的访问机制,通过索引值即可访问数据,在需要随机访问数据时可以选择List。
  • List是连续的数组空间存储数据,在遍历数据时有较快的速度。
  • List是连续的数组空间,Add()RemoveAt(int lastIndex)都是在数组尾部进行,尾部数据可以进行频繁操作。
  • List插入和移除数据会进行数组拷贝,所以不建议频繁使用Insert(int index, T item)RemoveAt(int index)方法,需要频繁在头部/中间插入删除可以使用LinkedList<T>

相关文档链接

List官方文档:https://learn.microsoft.com/zh-cn/dotnet/api/system.collections.generic.list-1?view=net-9.0

List源码地址:

https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs

相关文章:

  • Python 字典和集合(字典推导)
  • 美国NAB展会次日实况
  • C++ 基类的虚析构函数与派生的析构函数关系
  • Reflexion 框架 | 提示词工程(4)
  • HOW - React 组件渲染受其他无关数据影响和优化方案(含memo和props.children)
  • equals() 和 hashCode()
  • 泛目录站群,无极多功能泛目录站群程序:AI驱动的SEO增长引擎
  • java设计模式-单例模式
  • 【unity游戏开发入门到精通——动画篇】Animator2D序列帧动画
  • 解锁健康养生密码,拥抱活力人生
  • 手写数字识别实战教程:从零实现MNIST分类器(完整代码示例)
  • 算法篇(八)【递归】
  • 【代码随想录 字符串6.实现strstr】 KMP算法。
  • 1区6.6分CHARLS最新文章解读
  • 【学习笔记】文件上传漏洞--二次渲染、.htaccess、变异免杀
  • 2025年客运从业资格证备考刷题题库
  • 7-11 分段计算居民水费
  • 告别循环!用Stream优雅处理集合
  • AI无人直播教程 ai无人直播系统 【工具下载+教程】
  • 英语学习:单复数宏
  • 西安网站制作开发/著名的营销成功的案例
  • 建站之星导出网站/北京seo
  • 常德市 网站建设/推广普通话
  • 做旅游网站怎么融资/微信指数是搜索量吗
  • 网站域名.xin/网站seo招聘
  • 做网站的财务需求/网站推广优化