C#学习(十一)——Array和Collection

一、集合

集合重要且常用
孤立的数据是没有意义的,集合可以作为大量数据的处理,可进行数据的搜索、迭代、添加、删除。
C#中,所有集合都必须实现ICollection接口(数组Array除外)

集合说明
Array数组,固定长度、固定类型
ArrayList列表,可变长度、任意类型
List<T>列表,可变长度、固定类型
Dictionary<T>字典,键值对结构
Queue<T>队列,先进先出(FIFO)集合
Stack<T>栈,后进先出(LIFO)集合
IEnumerable<T>可迭代集合

C#集合的特点

  • [ 可以储存无限个元素(除了数组) ]
  • [ 任何一个集合都支持搜索、排序、复制、添加 、删除等操作 ]

二、数组

特点
1.固定长度
2.有明确顺序

使用数组是十分安全的,不会返回任何不存在的数据
例如:

class Program
{static void Main(string[] args){string[] daysOfWeek ={"Monday","Tuesday","Wednesday","Thuresday","Friday","Saturday","Sunday"};foreach (string day in daysOfWeek){Console.WriteLine(day);}//零索引 0-indexedConsole.WriteLine(daysOfWeek[0]);//周一Console.WriteLine(daysOfWeek[1]);//周二//固定长度string[] monthsOfYear = new string[12];monthsOfYear[0] = "January";monthsOfYear[1] = "February";monthsOfYear[2] = "March";monthsOfYear[3] = "April";monthsOfYear[4] = "May";monthsOfYear[5] = "June";monthsOfYear[6] = "July";monthsOfYear[7] = "August";monthsOfYear[8] = "September";monthsOfYear[9] = "October";monthsOfYear[10] = "November";monthsOfYear[11] = "December";Console.Read();}
}

三、列表与数组列表

1.List
在底层实现中,list依然使用数组承载数据,不过在数组装满数据以后,list会立刻创建新的数组来代替旧的数组,并且把所有数据复制装载到新的数组中,因此列表又可以成为动态数组。列表的容量不仅可以动态调整,也可以手动调整,对系统的动态调优取得很大帮助。
数组的访问速度略高于列表,但是列表对于空间的利用优于数组。

//List 列表
List<string> daysOfWeek2 = new List<string>();
daysOfWeek2.Add("Monday");
daysOfWeek2.Add("Tuesday");
daysOfWeek2.Add("Wednesday");
daysOfWeek2.Add("Thuresday");
daysOfWeek2.Add("Friday");
daysOfWeek2.Add("Saturday");
daysOfWeek2.Add("Sunday");
//只需要在前面加上I就可以声明接口的列表
IList<string> daysOfWeek3 = new List<string>();

2.ArrayList

List支持泛型,ArrayList不支持泛型,仅能保存对象

ArrayList装载数据很方便,但是提取数据较为麻烦,需要进行拆箱,影响性能

//ArrayList
var array = new ArrayList();
array.Add(daysOfWeek);
array.Add("123");
array.Add(1);

3.List的基本操作
List的有参数构造器

//List的有参数构造器
var daysOfWeek4 = new List<string>(daysOfWeek);//数组
var daysOfWeek5 = new List<string>(daysOfWeek2);//列表
var daysOfWeek6 = new List<string>(7);
List<string> daysOfWeek7 = new List<string>
{"Monday","Tuesday","Wednesday","Thuresday","Friday","Saturday","Sunday"
};

列表的插入数据,可以使用Insert或者InsertRange

//列表插入,Insert,InsertRange
daysOfWeek7.InsertRange(2, daysOfWeek);//将daysOfWeek插入到第二个位置后

如果要将列表7插入到列表6的最前面

daysOfWeek6.InsertRange(0, daysOfWeek7);

但是更推荐

daysOfWeek7.AddRange(daysOfWeek6);

因为使用Insert操作,会自动将原来的列表分成两个列表,在进行插入操作,影响性能。
删除数据

//删除数据,RemoveAt, RemoveRange
daysOfWeek7.RemoveAt(0);
daysOfWeek7.RemoveRange(2, 6);
daysOfWeek7.Remove("Monday"); //只删除遍历到的第一个数据
daysOfWeek7.RemoveAll(i => i == "Monday");//删除遍历到的所有的Monday
daysOfWeek7.RemoveAll(i => i.Contains("day"));//删除所有包含day的数据

四、迭代器Enumerator与循环遍历ForEach

读取列表

//读取列表
var a = daysOfWeek6.Count;//读取数据个数
var b = daysOfWeek6.Capacity;//读取列表容量

索引器,方括号就是索引器,准确查找位置

//索引器,方括号就是索引器,准确查找位置
var c = daysOfWeek6[3];

迭代器,将集合按照一定规律全部访问一遍

var enumerator = daysOfWeek6.GetEnumerator();
var d = enumerator.Current;//迭代器当前所指的元素(此时为空)
enumerator.MoveNext();//此时指向第一个元素,遍历完成返回TRUE
while (enumerator.MoveNext())
{Console.WriteLine(enumerator.Current);//使用while循环,会在最后输出一个null,是由于Current悬空
}
//为了解决上面问题,所有可以使用foreach
foreach ( var day in daysOfWeek6)
{Console.WriteLine(day);
}

Foreach遍历时不允许容量发生变化,所有元素均为只读数据,不允许修改

但是我们可以举个例子
Customers.cs

public class Customer{public Customer(int id, string name, string address){Id = id;Name = name;Address = address;}public int Id { get; set; }public string Name { get; set; }public string Address { get; set; }}

Program.cs

List<Customer> customers = new List<Customer>();customers.Add(new Customer(1, "小赵", "广州"));customers.Add(new Customer(2, "小钱", "北京"));customers.Add(new Customer(3, "小王", "上海"));customers.Add(new Customer(4, "小孙", "深圳"));foreach(var customer in customers){customer.Name = "123";Console.WriteLine(customer.Name);}

此时,使用foreach却可以修改数据,为什么?
原因很简单,这里的customer是引用类型的数据,对于foreach,引用类型的数据本身地址是不改变的,因此这里的数据就可以完成修改。

IEumerable<T>IEnumerator<T>

**由于不带泛型需要涉及到装箱拆箱,因此以下只讨论带泛型的版本 *

创建Bank类,使用迭代器遍历列表
Bank.cs

public class Bank : IEnumerable<Customer>
{public List<Customer> Customers { get; set; } = new List<Customer>();public Bank() {Customers.Add(new Customer(1, "小赵", "广州"));Customers.Add(new Customer(2, "小钱", "北京"));Customers.Add(new Customer(3, "小王", "上海"));Customers.Add(new Customer(4, "小孙", "深圳"));}public IEnumerator<Customer> GetEnumerator(){return Customers.GetEnumerator();}IEnumerator IEnumerable.GetEnumerator(){throw new NotImplementedException();}
}

Program.cs

var bank = new Bank();foreach (var item in bank)
{Console.WriteLine(item.Name);
}

下面,我们尝试自己创建一个迭代器,实现对于列表的遍历
创建MyEnumerator.cs

public class MyEnumerator<T> : IEnumerator<T>
{T[] _data;int _position = -1;//开始时,要让current悬空public MyEnumerator(T[] data){_data = data;}public T Current { get => _data[_position]; }object IEnumerator.Current { get => Current; }public void Dispose(){}public bool MoveNext(){_position++;return _position < _data.Length;}public void Reset(){_position = -1;}
}

MyList.cs

public class MyList<T> : IEnumerable<T>
{private T[] _data;int cuttentIndex;public MyList(int length){this._data = new T[length];cuttentIndex = 0;}public void Add(T item){_data[cuttentIndex] = item;cuttentIndex++;}public IEnumerator<T> GetEnumerator(){return new MyEnumerator<T>(_data);}IEnumerator IEnumerable.GetEnumerator(){throw new NotImplementedException();}
}

Bank.cs

public class Bank : IEnumerable<Customer>
{public MyList<Customer> Customers { get; set; } = new MyList<Customer>(4);public Bank() {Customers.Add(new Customer(1, "小赵", "广州"));Customers.Add(new Customer(2, "小钱", "北京"));Customers.Add(new Customer(3, "小王", "上海"));Customers.Add(new Customer(4, "小孙", "深圳"));}public IEnumerator<Customer> GetEnumerator(){return Customers.GetEnumerator();}IEnumerator IEnumerable.GetEnumerator(){throw new NotImplementedException();}
}

五、迭代与yield return

示例一个场景,有一百万个用户数据,我们需要调取前1000个客户数据

static void Main(string[] args)
{var customers = GetCustomers(1000000);foreach (var customer in customers){if(customer.Id < 1000){Console.WriteLine($"客户Id{customer.Id}, 客户姓名:{customer.Name}");}else{break;}}
}
static IEnumerable<Customer> GetCustomers(int count)
{var customers = new List<Customer>();for (int i = 0; i < count; i++){customers.Add(new Customer(i, $"Crackpot{i}", "天津"));}return customers;
}

此时,创建了1000000个数据,但是实际上只需要前1000个数据,对于其余的数据,内存空间完全浪费了,因为根本不会用到
因此,使用yield关键词,就可以实现一个懒加载的效果;yield语句的执行次数与if语句执行次数相同,可以极大地提升内存利用率。yield语句并不返回数据,而是返回数据的迭代。

static void Main(string[] args)
{var customers = GetCustomersYield(1000000);;foreach (var customer in customers){if(customer.Id < 1000){Console.WriteLine($"客户Id{customer.Id}, 客户姓名:{customer.Name}");}else{break;}}
}
static IEnumerable<Customer> GetCustomersYield(int count)
{var customers = new List<Customer>();for (int i = 0; i < count; i++){yield return new Customer(i, $"Crackpot{i}", "天津");

举例2

static void Main(string[] args)
{foreach (var i in Createnumerable()){Console.WriteLine(i);}
}
static IEnumerable<int> Createnumerable()
{yield return 3;yield return 2;yield return 1;
}

输出结果为3 2 1 ,可以看到yield可以实现非必要,不创建的原则

六、Benchmark性能基准测试

首先安装NuGet包
Benchmark
我们使用Benchmark来查看两种方法使用不使用yield的差距
创建BenchmarkTester.cs

[MemoryDiagnoser]
public class BenchmarkTester
{[Benchmark]public void ProcessCustomer(){var customers = GetCustomers(1000000);foreach (var customer in customers){if (customer.Id < 1000){Console.WriteLine($"客户id:{customer.Id}, 客户姓名:{customer.Name}");}else{break;}}}[Benchmark]public void ProcessCustomerYield(){var customers = GetCustomersYield(1000000);foreach (var customer in customers){if (customer.Id < 1000){Console.WriteLine($"客户id:{customer.Id}, 客户姓名:{customer.Name}");}else{break;}}}static IEnumerable<Customer> GetCustomersYield(int count){var customers = new List<Customer>();for (int i = 0; i < count; i++){yield return new Customer(i, $"Crackpot{i}", "天津");}}static IEnumerable<Customer> GetCustomers(int count){var customers = new List<Customer>();for (int i = 0; i < count; i++){customers.Add(new Customer(i, $"Crackpot{i}", "天津"));}return customers;}
}

然后再Program.cs中调用Benchmark进行测试var sumery = BenchmarkRunner.Run<BenchmarkTester>();
需要注意,需要在CMD中执行benchmark测试
找到项目文件,然后dotnet build -c Release
会生成文件于:项目名->bin->Release->net8.0->项目名.dll
dotnet 项目.dll
出现结果
测试结果
可以看到,使用yield的运行时间是不使用的大约1/2,而使用的内存分配仅为约1/671,足以看到yield对于系统运行性能的提升

七、数据搜索:字典

使用示例
program.cs

static void Main(string[] args)
{var customers = GetCustomerDictionary(1000000);customers.GetValueOrDefault(999999);var customer = customers[999999];Console.WriteLine($"客户id:{customer.Id}, 客户姓名:{customer.Name}");
}
static Dictionary<int, Customer> GetCustomerDictionary(int count)
{var customer = new Dictionary<int, Customer>();for (int i = 0; i < count; i++){customer.Add(i, new Customer(i, $"Crackpot{i}", "广州"));}return customer;
}

八、哈希表

C#中哈希表与字典几乎没有泛型,最显著区别是哈希表没有泛型而字典有泛型
哈希表的值均为object类型,因此难免会使用装箱或拆箱,因此非常耗时

static void Main(string[] args)
{var customerHashTable = GetCustomerHashTable(1000000);var customer = (Customer)customerHashTable[99999];Console.WriteLine($"客户id:{customer.Id}, 客户姓名:{customer.Name}");
}
static Hashtable GetCustomerHashTable(int count)
{var customer = new Hashtable();for (int i = 0; i < count; i++){customer.Add(i, new Customer(i, $"Crackpot{i}", "广州"));}return customer;
}

九、集合的交、并、差运算(HashSet)

HashSet并不常用,但是在处理一些特殊问题时,非常便捷。
示例为:查找公交线路系统
Program.cs

static void Main(string[] args)
{var database = new BusRouteRepository();Console.WriteLine("从哪里来?");string startingAt = Console.ReadLine();Console.WriteLine("到哪里去?");string goingTo = Console.ReadLine();var startingRoutes = database.FindBusTo(startingAt);var destination = database.FindBusTo(goingTo);HashSet<BusRoute> routes = new HashSet<BusRoute>(startingRoutes);routes.IntersectWith(destination);if(routes.Count > 0){foreach(var route in routes){Console.WriteLine($"乘坐公交车:{route}");}}else{Console.WriteLine("路线找不到");}Console.Read();
}

BusRoute.cs

public class BusRoute
{public int Number {  get; }public string Origin => PlacesServed[0];public string Destination => PlacesServed[^1];public string[] PlacesServed {  get; }public BusRoute(int number, string[] placesServed){this.Number = number;this.PlacesServed = placesServed;}public override string ToString() => $"{Number}: {Origin} -> {Destination}";public bool Serves(string destination){return Array.Exists(PlacesServed, place => place == destination);}
}

BusRouteRepository.cs

public class BusRouteRepository
{private readonly BusRoute[] _allRoutes;public BusRouteRepository(){_allRoutes = new BusRoute[]{new BusRoute(101, new string[] {"火车站","大学城","动物园","体育馆"}),new BusRoute(42, new string[] {"火车站","电子城","花园酒店","体育馆"}),new BusRoute(232, new string[] {"理工大学","海洋馆","购物中心","游泳馆","体育馆"}),new BusRoute(51, new string[] {"美食广场","长途汽车站","游乐园","机场"}),new BusRoute(6, new string[] {"南井村","双河营村","长途汽车站","火车站"}),};}public BusRoute[] FindBusTo(string location){return Array.FindAll(_allRoutes, route => route.Serves(location));}public BusRoute[] FindBusesBetween(string location1, string location2){return Array.FindAll(_allRoutes, route => route.Serves(location1) && route.Serves(location2));}
}

示例结果
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【GitHub项目推荐--基于 AI 的口语训练平台】【转载】

Polyglot Polyglot 是一个开源的基于 AI 的口语训练平台客户端&#xff0c;可以在 Windows、Mac 上使用。 比如你想练习英语口语&#xff0c;只需在该平台配置一个虚拟的 AI 国外好友&#xff0c;你可以通过发语音的方式和 AI 好友交流&#xff0c;通过聊天的方式提升你的口…

中仕教育:事业单位考试考什么?

事业单位考试分为两个阶段&#xff0c;分别是笔试和面试&#xff0c;考试科目包括公共科目和专业科目两部分。 公共科目内容是公共基础知识、职业能力测试或申论。一种形式为&#xff1a;公共基础知识职业能力测试或职业能力测试申论。另一种形式为&#xff1a;公共基础申论。…

c语言基础6

1.逗号表达式 逗号表达式&#xff0c;就是用逗号隔开的多个表达式。 逗号表达式&#xff0c;从左向右依次执行。整个表达式的结果是最后⼀个表达式的结果。 我们来看下面的一个代码&#xff1a; int main() {int a 1;int b 2;int ret (a > b, a b 2, b, b a 1);p…

Nginx实现反向代理负载均衡实验

实验环境&#xff1a; VM REdhat虚拟机&#xff08;192.168.87.5&#xff09;一台、VM Redhat虚拟机&#xff08;192.168.87.3&#xff09;一台、阿里云服务器&#xff08;47.93.79.92&#xff09;一台 实验要求&#xff1a;通过windows浏览器访问192.168.87.5&#xff08;虚…

《合成孔径雷达成像算法与实现》Figure5.19

clc clear close all距离向参数 R_eta_c 20e3; % 景中心斜距 Tr 25e-6; % 发射脉冲时宽 Kr 0.25e12; % 距离向调频率 Fr 7.5e6; % 距离向采样率 Nrg 256; % 距离线采样点数 Bw abs(Kr*Tr); …

仰暮计划|“她就是用她的一双小脚把我们兄弟姐妹几个拉扯大的”

在残存的一些老物件中&#xff0c;在一些泛黄的相片中&#xff0c;掩藏着岁月的冲刷和青葱的时光。曾经无忧无虑的少女早已白发苍苍&#xff0c;不复青春貌美&#xff1b;曾经在父母面前笑闹的孩子早已变成他人眼中的长辈。 ——题记 她的身影也许并不高大&#xff0c;甚至还略…

Linux编辑器vim(含vim的配置)

文章目录 前言vim的基本概念vim基本操作进入vim模式切换退出vim vim指令vim命令模式指令vim底行模式命令 简单vim配置 前言 本篇文章&#xff0c;小编将介绍Linux编辑器–>vim以及vim的配置。 vim的基本概念 正常/普通/命令模式(Normal mode) 控制屏幕光标的移动&#xf…

Gin 框架之jwt 介绍与基本使用

文章目录 一.JWT 介绍二.JWT认证与session认证的区别2.1 基于session认证流程图2.2 基于jwt认证流程图 三. JWT 的构成3.1 header : 头部3.2 payload : 负载3.2.1 标准中注册的声明 (建议但不强制使用)3.2.2 公共的声明3.2.3 私有的声明3.2.4 定义一个payload 3.3 signatrue : …

【计算机网络】概述|分层体系结构|OSI参考模型|TCP/IP参考模型|网络协议、层次、接口

目录 一、思维导图 二、计算机网络概述 1.计算机网络定义、组成、功能 2.计算机网络分类 3.计算机网络发展历史 &#xff08;1&#xff09;计算机网络发展历史1&#xff1a;ARPANET->互联网 &#xff08;2&#xff09;计算机网络发展历史2&#xff1a;三级结构因特网 …

jenkins对接K8S

创建连接K8S的凭据 查看需要使用到的命名空间 [rootk8s ~]# kubectl get ns |grep arts-system arts-system Active 16d创建service accounts [rootk8s ~]# kubectl create sa jenkins-k8s -n arts-system serviceaccount/jenkins-k8s created [rootk8s ~]# kubectl…

一键批量处理,轻松为HTML文本添加所需内容

你是否曾经遇到过需要批量处理大量HTML文本的情况&#xff1f;是否曾经因为一个个编辑而感到繁琐和无趣&#xff1f;现在&#xff0c;这些问题都可以得到轻松解决&#xff01;我们的文本批量处理工具&#xff0c;为你提供一站式的服务&#xff0c;让你告别重复繁琐的操作&#…

java eazyexcel 实现excel的动态多级联动下拉列表(2)使用MATCH+OFFSET函数

原理 同样是将数据源放到一个新建的隐藏的sheet中&#xff0c;第一行是第一个列表的数据&#xff0c;第二行是每一个有下级菜单的菜单&#xff0c;他下面的行就是他下级菜单的每一值使用MATCH函数从第二行找到上级菜单对应的列根据OFFSET函数从2中获取的列&#xff0c;取得下级…

深度学习之处理多维特征的输入

我们首先来看一个糖尿病的数据集&#xff1a; 在数据集中&#xff0c;我们称每一行叫做sample&#xff0c;表示一个样本&#xff0c;称每一列是feature&#xff0c;也就是特征在数据库里面这就是一个关系表&#xff0c;每一行叫做记录&#xff0c;每一列叫做字段。 每一个样本都…

2024年第一篇博客

这是2024年的第一篇博客&#xff0c;2023年笔者经历了一连串的生活、工作、学习上的转折和调整&#xff0c;跌跌撞撞时光飞逝&#xff0c;转眼间就踏着元旦的钟声步入了2024年&#xff0c;前思后想、辗转反侧、犹豫再三不知道从哪里开始博客新的篇章&#xff0c;这个问题坦诚说…

ARL灯塔vps云服务器安装

前提是vps服务器已经安装好docker 1、下载压缩包到本地 2、解除不能扫描edu等域名的限制 解压docker_arl.zip&#xff0c;打开docker_arl/config-docker.yaml文件 删除掉edu.cn等限制域名(图中已删除) 3、上传至vps云服务器 将docker_arl文件上传到云服务器opt目录下 这里我…

【智能家居】6、语音控制及网络控制代码实现

一、语音控制 1、指令结构体编写 这个结构体定义了一个命令输入的模型。在这个模型中,包含以下几个部分: cmdName:一个长度为128的字符串,用于存储命令名称。dvicesName:一个长度为128的字符串,用于存储设备名称。cmd:一个长度为32的字符串,用于存储具体的命令。Init:…

qt初入门6:QChar和QString相关接口练习

简单了解编码&#xff1a; ​ latin1&#xff08;ISO 8859-1&#xff09;字符集是对ASCII基本字符集的扩展&#xff0c;都是1字节编码。 Unicode编码有多重存储方案&#xff0c;utf-8使用1~4字节编码&#xff0c;最少1字节&#xff1b;utf-16使用2-4字节编码&#xff0c;最少2字…

数据湖技术之发展现状篇

一. 大数据处理架构&#xff1a; 大数据处理架构的发展过程具体可以分为三个主要阶段&#xff1a;批处理架构、混合处理架构&#xff08;Lambda、Kappa架构&#xff09;、湖仓一体。首先是随着Hadoop生态相关技术的大量应用&#xff0c;批处理架构应运而生&#xff0c;借助离线…

中国新能源汽车持续跑出发展“加速度”,比亚迪迎来向上突破

2023年已经过去&#xff0c;对于汽车圈而言&#xff0c;2023年是中国车市的分水岭&#xff0c;在这一年&#xff0c;中国汽车工业70年以来首次进入全球序列&#xff0c;自主品牌强势霸榜&#xff0c;销量首次超过合资车。要知道&#xff0c;这是自大众于1984年进入中国市场成立…

【面试】测试开发面试题

帝王之气&#xff0c;定是你和万里江山&#xff0c;我都护得周全 文章目录 前言1. 网络原理get与post的区别TCP/IP各层是如何传输数据的IP头部包含哪些内容TCP头部为什么有浮动网络层协议1. 路由协议2. 路由信息3. OSPF与RIP的区别Cookie与Session&#xff0c;Token的区别http与…