C#中List<T>存放元素的工作机制

 更新时间:2022年08月11日 08:46:38   作者:Darren Ji  
这篇文章介绍了C#中List<T>存放元素的工作机制,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

List<T>是怎么存放元素?我们扒一段List<T>的一段源码来一窥究竟。

using System;
using System.Diagnostic;
using System.Collections.ObjectModel;
using System.Security.Permissions;

namespace System.Collections.Generic
{
    ...
    [Serializable()]
    public class List<t> : IList<t>, System.Collections.IList
    {
        private const int _defaultCapacity = 4;
        private T[] _items; //List<T>内部是依靠数组_items存放数据的
        private int _size; //数组的长度
        private int _version;
        [NoSerialized]
        private Object _syncRoot;
        static T[] _emptyArray = new T[0];

        //无参数构造函数 把_items设置成一个空的数组
        public List()
        {
            _items = _emptyArray;
        }

        //此构造函数 给_items数组一个初始容量
        public List(int capacity)
        {
            ...
            items = new T[capaicty];
        }

        //此构造函数 把集合类型参数拷贝给_items数组
        public List(IEnumerable<t> collection)
        {
            ...
            ICollection<t> c = collection as ICollection<t>;
            if(c != null)
            {
                int count = c.Count; //把构造函数集合类型参数的长度赋值给临时变量count
                _items = new T[count]; //List<T>内部维护的_items数组的长度和构造函数集合类型参数的长度一致
                c.CopyTo(_items, 0); //把构造函数集合的所有元素拷贝到_items数组中去
                _size = count; //_items数组的长度就是构造函数集合类型参数的长度
            }
            else
            {
                _size = 0;
                _items = new T[_defaultCapacity];
                ...
            }
        }

        //通过设置这个属性,改变List<t>内部维护的_items数组的长度
        public int Capacity
        {
            get {return _items.Length; }
            set {
                if(value != _items.Length){ //如果当前赋值和List<t>维护的内部数组_items长度不一致
                    if(value < _size){
                        //TODO: 处理异常
                    }
                    if(value > 0){
                        T[] newItems = new T[value]; //创建一个临时的、新的数组,长度为新的赋值
                        if(_size > 0){
                            //把临时的、新的数组拷贝给List<t>内部维护的数组_items,注意,这时_items的长度为新的赋值
                            Array.Copy(_items, 0, newItems, 0, _size); 
                        }
                    } else {
                        _items = _emptyArray;
                    }
                }
            }
        }

        public void Add(T item)
        {
            if(_size == _items.Length) EnsureCapacity(_size + 1);
            _items[_size++] = item;
            ...
        }

        //确保List<t>内部维护的_items数组的长度至少是给定的值
        //如果_items数组原先的长度比给定的值小,就让_items数组的长度设置为原先的长度的2倍
        privat void EnsureCapacity(int min)
        {
            if(_items.Length < min){
                 int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Legnth * 2;
                 if(newCapacity < min) newCapacity = min;
                 Capacity = newCapacity;
            }
        }

    }
}

由此可见,向List<T>中存放元素的大致过程是这样的:

  • List<T>内部维护着一个数组_items,用来存放T类型的元素。
  • 当有新的T类型元素存放进来,即调用Add(T item)方法。
  • Add(T item)方法内部调用EnsureCapacity(int min)方法确保List<T>的Capaicty属性值至少在原先长度上加1,最多是原先长度的2倍。
  • 在给Capacity赋值的过程中,对_items的长度进行了扩容。
  • 扩容后,再把新的T类型元素存放进来。

简单地说:

当有新的元素存放到List<T>中时,List<T>先对其维护的内部数组进行扩容,然后再把新元素放进来。

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。如果你想了解更多相关内容请查看下面相关链接

相关文章

  • C#RSA对接JAVA中RSA方式代码实例

    C#RSA对接JAVA中RSA方式代码实例

    在本篇文章中小编给各位整理的是关于C#RSA对接JAVA中RSA方式代码实例,有需要的朋友们可以参考一下。
    2019-09-09
  • 深入Unix时间戳与C# DateTime时间类型互换的详解

    深入Unix时间戳与C# DateTime时间类型互换的详解

    本篇文章是对Unix时间戳与C# DateTime时间类型互换进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • 详解C#如何优雅地终止线程

    详解C#如何优雅地终止线程

    在大多情况下,我们只关心线程的创建与启动,运行,却并不关心线程的结束或者终止。今天这篇文章,我们就以一些简单的小例子,简述如何有效的停止线程,仅供学习分享使用,如有不足之处,还请指正
    2023-03-03
  • WPF微信聊天和通讯录按钮样式代码分享

    WPF微信聊天和通讯录按钮样式代码分享

    这篇文章主要为大家分享了WPF微信聊天和通讯录按钮样式代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • C#优化if...else代码的方案总结

    C#优化if...else代码的方案总结

    在编写代码实现业务需求过程中,会使用到大量的if...else 判断语句,随业务复杂程度不同,导致判断语句出现多层嵌套、多分支等情况,导致代码可读性变差、增加维护难度,本文介绍了C# 如何优化 if...else 让代码优雅起来,需要的朋友可以参考下
    2024-06-06
  • c#linq里的Skip和Take实现分页或遍历

    c#linq里的Skip和Take实现分页或遍历

    LINQ的优势在于它提供了一种直观、类型安全的方式来操作各种类型的数据,查询常需要获取一部分数据,为了实现这一功能,LINQ提供了Take 和Skip运算符,Take运算符用于从一个序列中返回指定个数的元素,Skip运算符用于从一个序列中跳过指定个数的元素
    2024-01-01
  • 基于C#中XmlReader读取Xml的深入分析

    基于C#中XmlReader读取Xml的深入分析

    本篇文章是对C#中XmlReader读取Xml进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 详解C#中对于接口的实现方式(隐式接口和显式接口)

    详解C#中对于接口的实现方式(隐式接口和显式接口)

    这篇文章主要介绍了详解C#中对于接口的实现方式(隐式接口和显式接口),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • visio二次开发--判断文档是否已发生变化(变化就加星号*)

    visio二次开发--判断文档是否已发生变化(变化就加星号*)

    最近做一个故障树诊断的项目,用visio二次开发,可以同时打开多个绘制的故障树图形文档。项目中需要实现判断文档是否发生变化,这是很多编辑软件的基本功能,变化了就加个星号*
    2013-04-04
  • C#学习教程之Socket的简单使用

    C#学习教程之Socket的简单使用

    这篇文章主要给大家介绍了关于C#学习教程之Socket的简单使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-02-02

最新评论