C#中List<T>底层原理剖析

C#中List底层原理剖析

  • 1. 基础用法
  • 2. List的Capacity与Count:
  • 3.List的底层原理
    • 3.1. 构造
    • 3.2 Add()接口
    • 3.3 Remove()接口
    • 3.4 Inster()接口
    • 3.5 Clear()接口
    • 3.6 Contains()接口
    • 3.7 ToArray()接口
    • 3.8 Find()接口
    • 3.8 Sort()接口
  • 4. 总结
  • 5. 参考

人物

1. 基础用法

list.Max() 取最大元素

list.Avgage() 取平均值

static void Main()
{
    List<string> names = new List<string>();
    names.Add("一号元素");
    names.Add("二号元素");
    names.Add("狗");
    names[2] = "三号元素";//修改第三个元素

    string[] str = new string[] { "四号元素", "五号元素", "六号元素" };
    names.AddRange(str);//为集合增加数组

    Console.WriteLine("当前集合元素个数为{0}",names.Count);//返回集合元素个数

    Console.WriteLine("当前集合的容量为{0}", names.Capacity);//返回当前集合的容量
    //当添加元素的时候集合的容量不足以容纳所有元素就会自动增加目前元素数一倍的容量。

    Console.WriteLine(names.Contains("三号元素"));//返回集合中是否存在某元素,bool类型

    names.IndexOf("三号元素");//返回元素的索引值

    names.Clear();//清空所有元素,元素个数为0,但是容量不变
}

2. List的Capacity与Count:

  • Count 属性表示 List 中实际包含的元素数量。它是一个只读属性。
  • Capacity 属性表示 List 内部数组的容量,即它可以容纳的元素的数量。容量是指分配给列表的内部数组的大小,而不是列表中实际包含的元素数量。
  • 当添加元素时,如果内部数组的容量不够,List 会自动调整容量以容纳更多的元素。
List<int> list1= new List<int>();
WriteLine($"List的初始容量:{list1.Capacity}"); 
WriteLine($"List的初始数量:{list1.Count}");
list1.Add(0);
WriteLine($"通过Add()添加一个元素后的容量:{list1.Capacity}");
WriteLine($"通过Add()添加一个元素后的数量:{list1.Count}");

以上代码输出:

List的初始容量:0
List的初始数量:0
通过Add()添加一个元素后的容量:4
通过Add()添加一个元素后的数量:1

3.List的底层原理

3.1. 构造

private class List<T> : Ilist<T>, System.Collections.IList, IReadOnlyList<T>

List内部是由数组实现的,当没有指定额定容量时,初始容量为0。

3.2 Add()接口

将给定对象添加到此列表的末尾。每次增加元素前,都会判断数组容量是否够。不够则进行扩容

public void Add(T item)
  • 每次扩容都会扩充一倍。数组初始值为0,扩容初始值为4,因此整个扩容路线是:0-4-8-16-32-64-125-256……
  • 每次扩容都会申请新的空间,然后进行元素拷贝,然后还要回收旧空间。会给GC造成不小的负担。
  • 另外还有浪费空间的缺点。比如存放126个元素时,会扩容到256的空间,剩下的130个空间就浪费了。

3.3 Remove()接口

删除列表中首次出现的item。将从头到尾搜索,使用Object.Equal()进行相等的判断。

public bool Remove(T item)
  • 找到要删除的位置后,使用Array.Copy()进行覆盖;
  • 时间复杂度O(n);
  • 删除一个元素后,内部数组的容量不变。

3.4 Inster()接口

功能:在给定索引处插入元素。每次插入元素前都会检查当前容量是否足够,如果不够则进行扩容。

public void Insert(int index, T item);
  • 在插入元素时,采用的也是复制数组的形式,将数组指定索引后面的元素向后移动一个位置。
  • 与Add()类似,会给GC产生负担,并且存在空间浪费的问题。

3.5 Clear()接口

功能:清除列表内容。注意:只是将Count置,之前申请空间不变,也就是Capacity不变。

List<int> list1= new List<int>();
list1.Add(0);
WriteLine($"通过Add()添加一个元素后的容量:{list1.Capacity}"); 
WriteLine($"通过Add()添加一个元素后的数量:{list1.Count}");
list1.Clear();
WriteLine($"Clear后的容量:{list1.Capacity}"); //清除后容量不变。即之前申请的空间不变
WriteLine($"Clear后的数量:{list1.Count}"); //数量置0

以上输出:

通过Add()添加一个元素后的容量:4
通过Add()添加一个元素后的数量:1
Clear后的容量:4
Clear后的数量:0

3.6 Contains()接口

功能:确定某元素是否在List中。

public bool Contains(T item)
  • 使用线性查找的方式,挨个比较元素。

3.7 ToArray()接口

复制列表到一个新数组,O(n)的操作。

public T[] ToArray()

3.8 Find()接口

功能:查找满足指定条件的元素。接受一个Predicate委托作为参数,该委托定义了要应用于每个元素的条件。

public T Find(Predicate<T> match)
  • 线性查找的方式,挨个比较,返回满足条件的第一个元素。
  • 用法:
    List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
    // 使用Find函数查找大于3的第一个元素
    int result = numbers.Find(x => x > 3); //Lambda表达式x => x > 3表示条件
    Console.WriteLine(result);  // 输出: 4
    

3.8 Sort()接口

功能:对列表中的元素进行排序。Sort 方法使用元素的默认比较器进行排序,或者可以传递一个自定义的比较器作为参数。

public void Sort(int index, int count, TComparee<T> comparer)
  • index与count是指定排序区间。不指定的话则整个列表进行排序;
  • 用法
    List<int> numbers = new List<int> { 5, 2, 8, 1, 3 };
    numbers.Sort();
    numbers.Sort((x, y) => y.CompareTo(x));// 使用自定义比较器对列表进行降序排序
    
  • 该方法在原地修改列表,而不是返回排序后的列表。
  • 排序时使用Array.Sort()方法进行排序,该方法内部采用了快速排序,效率是O(nlogn).

4. 总结

  1. List的效率并不高,甚至比数组还差,只是通用性强而已;
  2. List 的内存分配方式也不合理。当List 里的元素不断增加时,会多次重新分配数组,导致原来的数组被抛弃,造成回收的压力。
  3. 对于第2点的问题,我们可以在创建List 实例时提前告知 List 对象最多会有多少元素在里面,这样 List 就不会因为空间不够而抛弃原有的数组去重新申请教组了。例如:
    List<int> list11 = new List<int>(128);
    WriteLine($"容量:{list11.Capacity}"); //容量:128
    WriteLine($"数量:{list11.Count}"); //数量:0
    
  4. List是线程不安全的。
    • 当多个线程同时访问和修改同一个 List 实例时,可能会导致不可预测的结果或发生错误。
    • 例如,并发读写的问题。一个线程正在读取列表的元素,而另一个线程在同时修改列表,这可能导致读取到无效或不正确的数据。
    • 可使用同步机制解决该问题。lock语句或其他同步机制来保护对 List 的并发访问。确保同一时间只有一个线程访问列表。

5. 参考

List源码地址:链接: https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/299758.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简单又好玩的数据库就是有点烦

1 数据库 1.1 数据库类型 关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向于记录。 SQL语句&#xff08;标准数据查询语句&#xff09;就是一种基于关系型数据库的语言&#xff…

基于Java SSM框架实现实现机房预约系统项目【项目源码+论文说明】

基于java的SSM框架实现机房预约系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#…

2024年度 ROTS - 实时操作系统 Top 15

RTOS&#xff08;实时操作系统&#xff09;。 这里说的 RTOS 并非新星球大战电影中的机器人&#xff0c;而是物联网设备、航空系统、空中交通管制等背后的无声协调者&#xff0c;就在地球上。 RTOS&#xff0c;或称实时操作系统&#xff0c;设计它们是为了更好的管理资源&…

Transformer-MM-Explainability

two modalities are separated by the [SEP] token&#xff0c;the numbers in each attention module represent the Eq. number. E h _h h​ is the mean&#xff0c; ∇ \nabla ∇A : ∂ y t ∂ A {∂y_t}\over∂A ∂A∂yt​​for y t y_t yt​ which is the model’s out…

分布式锁3: zk实现分布式锁2 使用临时节点(需要自旋)

一 使用临时节点实现分布式锁 1.1 代码截图 1.2 代码如下 由于zookeeper获取链接是一个耗时过程&#xff0c;这里可以在项目启动时&#xff0c;初始化链接&#xff0c;并且只初始化一次。借助于spring特性&#xff0c;代码实现如下&#xff1a; package com.atguigu.distri…

营业执照代办网站源码 工商注册代账公司模板源码 公司注册和企业资质业务办理网站

这款大气蓝色的网站响应式模板专门为工商记账、公司注册和企业资质业务办理公司设计,包含了16个页面,非常适合正在寻找一个高质量网站模板的企业使用。 该模板使用响应式设计,使其能够在各种设备上呈现良好的用户体验,包括PC、平板和手机等。同时,页面设计非常美观,允许…

JAVA基础语句1

目录 前言 一.JAVA特性 简单 面向对象 分布式 多线程 二.关键字 三.对象和类 对象 类 构造方法 创建对象 访问实例变量和方法 源文件声明规则 Java 包 import 语句 总结 前言 这里参考了&#xff1a;Java 教程 | 菜鸟教程 (runoob.com) 第一个必须是&#xff1a; hello world&a…

【langchain】入门初探实战笔记(Chain, Retrieve, Memory, Agent)

1. 简介 1.1 大语言模型技术栈 大语言模型技术栈由四个主要部分组成&#xff1a; 数据预处理流程&#xff08;data preprocessing pipeline&#xff09;嵌入端点&#xff08;embeddings endpoint &#xff09;向量存储&#xff08;vector store&#xff09;LLM 终端&#xff…

看图识熊(三)

使用Windows Machine Learning加载ONNX模型并推理 环境要求 Windows Machine Learning支持在Windows应用程序中加载并使用训练好的机器学习模型。Windows 10从10.0.17763.0版本开始提供这套推理引擎&#xff0c;所以需要安装17763版本的Windows 10 SDK进行开发&#xff0c;并…

XML技术分析01

一、什么是XML eXtensible Markup Language ——可扩展标记语言 1、标记&#xff08; markup &#xff09;及标记语言&#xff1a; markup的含义是指插入到文档(document)中的标记 标记&#xff1a;是以标志的格式附加在文档中的。标志赋予文档的某一部分一个标记的标识…

HTML5+CSS3小实例:人物介绍卡片2.0

实例:人物介绍卡片2.0 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><…

PDF控件Spire.PDF for .NET【安全】演示:获取并验证 PDF 中的数字签名

在 PDF 中创建数字签名广泛用于保护 PDF 文件。因此&#xff0c;当您查看一些带有数字签名的PDF文件时&#xff0c;需要获取并验证数字签名。本文向您展示了一种通过使用Spire.PDF和 C# 代码来获取和验证 PDF 中的数字签名的解决方案。 Spire.PDF for .NET 是一款独立 PDF 控件…

搭建一个教育小程序的必要步骤

随着科技的飞速发展&#xff0c;小程序已经深入到我们生活的方方面面。对于教育行业来说&#xff0c;小程序的出现不仅为教育机构提供了新的宣传和互动平台&#xff0c;更为学生和家长带来了更为便捷的学习体验。那么&#xff0c;如何开发一款适合教育机构的小程序呢&#xff1…

基于springboot的sql防注入过滤器

目录 何为SQL注入基于springboot的sql防注入过滤器 回到顶部 何为SQL注入 SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严&#xff0c;攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句&#xff0c;在管理员不知情的情况下实现…

牛客网BC12-字符圣诞树

字符圣诞树 解题思路&#xff1a; 确定行数&#xff0c;一共5行&#xff0c;循环5次确定每行答应的内容&#xff0c;分成两部分&#xff0c;空格和字符 打印空格的个数依次递减打印字符的个数依次递增 找出打印空格和字符的个数与行数之间的关系 int main() {char ch 0;scanf(…

C# StringBuilder对比string的优点和15大案例

文章目录 StringBuilder和String 对比1. **循环内字符串连接**2. **构建大型日志消息**3. **格式化长字符串**4. **SQL 查询构造**5. **从文件读取并合并行**6. **拼接数组元素**7. **格式化电子邮件模板**8. **处理用户输入流**9. **JSON 或 XML 格式的序列化与构建**10. **动…

静态网页设计——天行九歌(HTML+CSS+JavaScript)(dw、sublime Text、webstorm、HBuilder X)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a;https://www.bilibili.com/video/BV1de411m7y4/?vd_source5f425e0074a7f92921f53ab87712357b 源码&#xff1a;https://space.bilibili.com…

光明源@智慧环卫新标杆,智慧公厕系统全面升级

在城市的喧嚣之中&#xff0c;一个看似不太引人注目但却无法忽视的领域正在经历一场革命性的变革。作为城市基础设施的一部分&#xff0c;公厕一直以来被人们忽略&#xff0c;然而&#xff0c;随着科技的迅速发展&#xff0c;一项前所未有的升级正向我们走来——智慧公厕系统。…

2024.1.7周报

目录 摘要 ABSTRACT 一、文献阅读 1、题目 2、摘要 3、模型架构 4、文献解读 一、Introduction 二、创新点 三、实验过程 四、结论 二、深度学习知识 一、从Encoder-Decoder框架中理解为什么要有Attention机制 二、Attention思想 三、Seq2Seq Attention代码逐…

【C++】- 类和对象(!!C++类基本概念!this指针详解)

类和对象 引入类类的定义类的访问限定操作符类的作用域类的实例化类对象模型this指针 引入类 在 C中&#xff0c;引入了一个新的定义----------类。类是一种用户自定义的数据类型&#xff0c;用于封装数据和行为。类可以看作是一个模板或蓝图&#xff0c;描述了一组相关的数据和…