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

C# 常用数据结构之列表List<T>

List<T> 是 C# 中使用非常频繁的一种数据结构,我习惯称之为“列表”!

前面整理了一下数组的用法,我们可以发现数组有一个致命的缺陷,那就是固定长度,这就导致了数组的使用范围比较有限。

List<T> 和 ArrayList 就解决了这个问题,这两种数据结构本质上都是数组,但他们是“动态数组”,长度可变!

不过 ArrayList 是属于被抛弃的那种,一般都不怎么被使用的,Why?

因为 ArrayList 有一个骚操作,就是会把所有元素都当做 Object 处理,这就导致在对值类型的元素进行操作的时候会进行装箱和拆箱,如果装箱和拆箱比较频繁,性能有点刚不住!

而 List<T> 作为 ArrayList 的泛型等效类,加上了类型 T 的限制,不仅解决了装箱拆箱导致的性能问题,同时也保证了类型安全!

so,可以忽略掉 ArrayList 这个数据结构了,因为 ArrayList 有的 List<T> 全都有,还补足了各种缺陷!

好了,回归正题,看看 List<T> 到底该怎么用~

List<T> 的命名空间:System.Collections.Generic

特性

1、顺序存储,改查快,增(插入)删慢;

  • 使用 Add 将新元素添加在末尾是很快的,但是使用 Insert 将新元素添加在其他位置就不同了。因为这样会导致后面元素的移动位置(后移),删除同样道理,会导致后面元素的位置前移。这样效率是很低的!

2、长度可变,容量不够会自动扩容;

  • 每次扩容,容量增加一倍。

3、需要指定数据类型,保证类型安全;

  • 同时也避免了 ArrayList 可能出现的装箱拆箱操作。

初始化

List<T> 的初始化有以下三种方式:

1、List<T>() 初始化 List<T> 类的新实例,该实例为空并且具有默认初始容量。

List<int> intList = new List<int>();
Console.WriteLine("intList 的默认容量为:{0}", intList.Capacity);
intList.Add(1);
intList.Add(2);
intList.Add(3);
foreach(var value in intList)
{
    Console.Write(value + " ");
}
Console.WriteLine();
Console.WriteLine("intList 的当前容量为:{0}", intList.Capacity);
/*
intList 的默认容量为:0
1 2 3
intList 的当前容量为:4
*/

ps

- 当向 List<T> 中添加元素时,如果容量不够会自动扩容,扩容会新建一个数组,将原数组里面的数据拷贝进去;

- 当从 List<T> 中删除元素后,可以通过调用 TrimExcess 方法或通过显式设置属性来减少容量 Capacity,减少容量会重新分配内存,并拷贝 List<T> 中的所有元素。

2、List<T>(IEnumerable<T>) 初始化 List<T> 类的新实例,该实例包含从指定集合复制的元素并且具有足够的容量来容纳所复制的元素。

int[] intArray = {1, 2, 3};
List<int> intList = new List<int>(intArray);
foreach(var value in intList)
{
    Console.Write(value + " ");
}
// 1 2 3

也可以使用这种简化的写法:

List<int> intList = new List<int> {1, 2, 3};
foreach(var value in intList)
{
    Console.Write(value + " ");
}
// 1 2 3

3、List<T>(Int32) 初始化 List<T> 类的新实例,该实例为空并且具有指定的初始容量。

List<int> intList = new List<int>(16);
Console.WriteLine("intList 的当前容量为:{0}", intList.Capacity);  // intList 的当前容量为:16

ps

如果集合的大小为可估算值,建议使用这种初始化方式给 List<T> 指定一个合适的容量,避免在插入元素的时候动态扩容造成的性能消耗!

常用属性

Capacity

获取或设置该内部数据结构在不调整大小的情况下能够容纳的元素总数。

List<int> intList = new List<int> {1, 2, 3};
Console.WriteLine("intList 的当前容量为:{0}", intList.Capacity);  // intList 的当前容量为:4
intList.Capacity = 3;
Console.WriteLine("intList 的当前容量为:{0}", intList.Capacity);  // intList 的当前容量为:3
intList.Add(4);
Console.WriteLine("intList 的当前容量为:{0}", intList.Capacity);  // intList 的当前容量为:6

容量始终大于或等于元素的数量。如果在添加元素的时候元素的数量超过了容量,则会在复制旧元素并添加新元素之前自动重新分配内部数组,从而增加容量(自动扩容)。

Count

获取 List<T> 中包含的元素数量。

List<int> intList = new List<int> {1, 2, 3};
Console.WriteLine("intList 中的元素数量为:{0}", intList.Count);  // intList 中的元素数量为:3

常用方法

Add

将对象添加到 List<T> 的结尾处。

List<string> strList = new List<string>();
strList.Add("a");
strList.Add("b");
strList.Add("c");
foreach(var element in strList)
{
    Console.Write(element + " ");
}
// a b c

Clear

从 List<T> 中移除所有元素。

List<bool> boolList = new List<bool> {false, true, false};
Console.WriteLine("intList 中的元素数量为:{0}", boolList.Count);  // intList 中的元素数量为:3
boolList.Clear();
Console.WriteLine("intList 中的元素数量为:{0}", boolList.Count);  // intList 中的元素数量为:0
Console.WriteLine("intList 的当前容量为:{0}", boolList.Capacity);  // intList 的当前容量为:4

ps

清空或删除元素不会改变 List<T> 的容量,只会主动扩容,不会主动缩容!

Contains

确定某元素是否在 List<T> 中。

List<int> strList = new List<int> {1, 666, 3};
Console.WriteLine(strList.Contains(666) ? "元素存在" : "元素不存在");  // 元素存在

Find

搜索与指定谓词所定义的条件相匹配的元素,并返回整个 List<T> 中的第一个匹配元素。

List<int> intList = new List<int> {1, 2, 3};
int ret = intList.Find(x => x > 2);
Console.WriteLine(ret);  // 3

ps

Find系还有几个方法:FindAll、FindIndex、FindLast、FindLastIndex,这里就不一一赘述了!

IndexOf

返回 List<T> 或它的一部分中某个值的第一个匹配项的从零开始的索引。

List<string> strList = new List<string> {"a", "b", "c"};
Console.WriteLine(strList[1]);  // b
Console.WriteLine(strList.IndexOf("b"));  // 1

Insert

将元素插入 List<T> 的指定索引处。

List<int> intList = new List<int> {1, 2, 3};
intList.Insert(1, 666);
intList.Insert(2, 888);
foreach(var element in intList)
{
    Console.Write(element + " ");
}
// 1 666 888 2 3

ps

插入操作会导致从插入下标位置开始往后的元素后移。

Remove

从 List<T> 中移除特定对象的第一个匹配项。

List<int> intList = new List<int> {1, 1, 2};
intList.Remove(1);
foreach(var element in intList)
{
    Console.Write(element + " ");
}
// 1 2

ps

删除操作会导致删除位置之后的元素前移。

RemoveAll

移除与指定的谓词所定义的条件相匹配的所有元素。

List<int> intList = new List<int> {1, 2, 3};
intList.RemoveAll(x => x > 1);  // 删除所有大于1的元素
foreach(var element in intList)
{
    Console.Write(element + " ");
}
// 1

RemoveAt

移除 List<T> 的指定索引处的元素。

List<int> intList = new List<int> {1, 2, 3};
intList.RemoveAt(1);  // void
foreach(var element in intList)
{
    Console.Write(element + " ");
}
// 1 3

Reverse

将 List<T> 或它的一部分中元素的顺序反转。

List<int> intList = new List<int> {1, 2, 3};
intList.Reverse();
foreach(var element in intList)
{
    Console.Write(element + " ");
}
// 3 2 1

TrimExcess

将容量设置为 List<T> 中元素的实际数目(如果该数目小于某个阈值)。

List<int> intList = new List<int>(16);
Console.WriteLine("intList 的当前容量为:{0}", intList.Capacity);  // intList 的当前容量为:16
intList.Add(1);
intList.Add(2);
intList.Add(3);
intList.TrimExcess();
Console.WriteLine("intList 的当前容量为:{0}", intList.Capacity);  // intList 的当前容量为:3

Sort

使用指定或默认的 IComparer<T> 实现或提供的 Comparison<T> 委托对 List<T> 中的元素或部分元素进行排序,以比较列表元素。

1、使用默认比较器对整个 List<T> 中的元素进行排序。

List<int> intList = new List<int> {4, 5, 1, 3, 2};
intList.Sort();
foreach(var element in intList)
{
    Console.Write(element + " ");
}
// 1 2 3 4 5

ps

- 默认升序;

- 不稳定排序;

  • 即如果两个元素相等,则可能不会保留它们的顺序,相反,稳定排序会保留相等元素的顺序。

- 应用反省排序,规则如下:

  • 如果分区大小小于或等于16个元素,则它将使用插入排序算法。
  • 如果分区数超过2个 log n,其中 n 是输入数组的范围,则使用 Heapsort 算法。
  • 否则,它将使用快速排序算法。

2、自定义排序

List<int> intList = new List<int> {2, 4, 1, 5, 3};
intList.Sort((x, y) => {
    // return x.CompareTo(y);  // 升序
    return y.CompareTo(x);  // 降序
});
foreach(var element in intList)
{
    Console.Write(element + " ");
}
// 5 4 3 2 1

参考:

List<T> 类

集合和数据结构

相关文章:

  • 组织之间的关系就好像是个大家庭
  • C# 常用数据结构之字典Dictionary<TKey,TValue>
  • 领导作为“先知者”需知的九大管理之道
  • C# 列表List<T>排序整理
  • 企业在经营时 要为自己“挖一口井”
  • C# 字典Dictionary<TKey,TValue>排序整理
  • 协同软件市场一盘散沙 春种能否秋收心中没底
  • bat 与或非
  • 独立软件开发商,出路何在?
  • Lua实战之不使用循环逆序输出一个数组
  • VS Code快速生成C#注释头
  • 10个装机最贵与免费的十大软件下载
  • VS 和 VS Code 更换字体
  • .NET值类型变量“活”在哪?
  • Lua do-end
  • Date型的使用
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • JavaScript 奇技淫巧
  • Java比较器对数组,集合排序
  • Java的Interrupt与线程中断
  • MaxCompute访问TableStore(OTS) 数据
  • mysql 5.6 原生Online DDL解析
  • MySQL的数据类型
  • python学习笔记 - ThreadLocal
  • Sass 快速入门教程
  • STAR法则
  • Yii源码解读-服务定位器(Service Locator)
  • 百度地图API标注+时间轴组件
  • - 概述 - 《设计模式(极简c++版)》
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 使用agvtool更改app version/build
  • 协程
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • 正则表达式-基础知识Review
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • #NOIP 2014# day.2 T2 寻找道路
  • #控制台大学课堂点名问题_课堂随机点名
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (2)STM32单片机上位机
  • (C语言)fread与fwrite详解
  • (js)循环条件满足时终止循环
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (附源码)spring boot儿童教育管理系统 毕业设计 281442
  • (十) 初识 Docker file
  • (五)关系数据库标准语言SQL
  • (转)winform之ListView
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .Net语言中的StringBuilder:入门到精通
  • .net中调用windows performance记录性能信息
  • .vue文件怎么使用_我在项目中是这样配置Vue的
  • []使用 Tortoise SVN 创建 Externals 外部引用目录
  • [1]-基于图搜索的路径规划基础