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-indexed
        Console.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));
    }
}

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

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

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

相关文章

【Linux】进程间通信概念 | 匿名管道

文章目录 一、什么是进程间通信进程间通信的概念进程间通信的目的进程间通信的分类进程间通信的本质 二、什么是管道三、匿名管道匿名管道的原理✨站在内核角度理解管道✨站在文件描述符角度理解管道 pipe系统调用fork后在父子进程间使用管道通信代码实现 匿名管道的读写规则管…

初识人工智能,一文读懂机器学习之逻辑回归知识文集(7)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

CSS 双色拼接按钮效果

<template><view class="sss"><button> <!-- 按钮 --><view class="span"> 按钮 </view> <!-- 按钮文本 --></button></view></template><script></script><style>body {b…

uniapp微信小程序-input默认字的样式

需要的是这样的 问题 正常是在input框上面写样式就行&#xff0c;但是uniapp不起作用 解决 直接在input上写placeholder-style"color就解决了 <input class"findInput" type"text" placeholder"关键词查询"placeholder-style"co…

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 : …

一文掌握SpringBoot注解之@Component 知识文集(5)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

操作系统(5)-----操作系统进程相关

目录 一.进程的组成 1.PCB&#xff08;进程控制块&#xff09; 2.程序段与数据段 二.进程的特征 三.进程的状态以及状态的转换 四.进程的组织 1.链接方式 2.索引方式 五.进程控制 六.进程控制相关原语 1.创建原语 2.撤销原语 3.阻塞原语 4.唤醒原语 5.切换原语 …

Spring MVC 请求流程

SpringMVC 请求流程 一、DispatcherServlet 是一个 Servlet二、Spring MVC 的完整请求流程 Spring MVC 框架是基于 Servlet 技术的。以请求为驱动&#xff0c;围绕 Servlet 设计的。Spring MVC 处理用户请求与访问一个 Servlet 是类似的&#xff0c;请求发送给 Servlet&#xf…

5G赋能智慧文旅:科技与文化的完美结合,打造无缝旅游体验,重塑旅游业的未来

一、5G技术&#xff1a;智慧文旅的强大引擎 5G技术的起源可以追溯到2010年&#xff0c;当时世界各国开始意识到4G技术已经达到了瓶颈&#xff0c;无法满足日益增长的移动通信需求。2013年&#xff0c;国际电信联盟&#xff08;ITU&#xff09;成立了5G技术研究组&#xff0c;开…

力扣516. 最长回文子序列

动态规划 思路&#xff1a; 字符串最长回文子序列问题可以转换为原字符串 s 和逆串 s 的最长公共子序列长度问题&#xff0c;具体推断过程可以参考 力扣1312. 让字符串成为回文串的最少插入次数问题变成了求两个字符串最长公共子序列长度问题&#xff0c;具体思路可以参考 力扣…

物联网协议Coap之C#基于Mozi的CoapClient调用解析

目录 前言 一、CoapClient相关类介绍 1、CoapClient类图 2、CoapClient的设计与实现 3、SendMessage解析 二、Client调用分析 1、创建CoapClient对象 2、实际发送请求 3、Server端请求响应 4、控制器寻址 总结 前言 在之前的博客内容中&#xff0c;关于在ASP.Net Co…

《向量数据库指南》——Milvus Cloud 2023 年关键数据

10 倍 - 内存节省 在 2023 年,RAG 模型被广泛认为是向量数据库的最主要应用之一。我们发现,在文档被分割成块之后,一个含有 500 个 Token 的 Chunk(通常 1000 字节)会转换成一个 1536 维的 float32 向量(通常 6000 字节),这意味着向量数据的体积可能会超过原始文档的…

Linux服务器配置与管理(第三次实验)

实验目的及具体要求 目的 1.熟悉Shell 脚本语法 2.掌握创建脚本的方法 3.掌握运行脚本的方法 4.掌握变量及表达式 5.掌握Shell 控制结构 6.掌握Shell 函数 任务 1.显示当前日期时间、执行路径、用户账户及所在的目录位置 2.判断一个文件是不是字符设备文件&#xff0…

Github 2024-01-28 开源项目日报Top10

根据Github Trendings的统计&#xff0c;今日(2024-01-28统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目3TypeScript项目2Rust项目1HTML项目1JavaScript项目1Cuda项目1C#项目1非开发语言项目1 Nuxt&#…

有手就行!阿里云上3分钟搞定幻兽帕鲁联机服务器搭建

幻兽帕鲁最近在社区呈现了爆火的趋势&#xff0c;在线人数已突破百万级别&#xff0c;官方服务器也开始出现不稳定&#xff0c;卡人闪退的情况。对于有一定财力的小伙伴&#xff0c;搭建一个私人服务器是一个最稳定而舒服的解决方案。 本文萝卜哥将讲解一下如何快速搭建 palwo…

Nginx解析漏洞(nginx_parsing_vulnerability)

目录 Nginx解析漏洞 环境搭建 复现 漏洞利用 Nginx解析漏洞 NGINX解析漏洞主要是由于NGINX配置文件以及PHP配置文件的错误配置导致的。这个漏洞与NGINX、PHP版本无关&#xff0c;属于用户配置不当造成的解析漏洞。具体来说&#xff0c;由于nginx.conf的配置导致nginx把…

【Java】Spring注解开发

一、Spring注解开发 1 注解开发定义Bean对象【重点】 目的&#xff1a;xml配置Bean对象有些繁琐&#xff0c;使用注解简化Bean对象的定义 问题导入 问题1&#xff1a;使用什么标签进行Spring注解包扫描&#xff1f; 问题2&#xff1a;Component注解和Controller、Service、R…

MQ面试题之Kafka

前言 前文介绍了消息队列相关知识&#xff0c;并未针对某个具体的产品&#xff0c;所以略显抽象。本人毕业到现在使用的都是公司内部产品&#xff0c;对于通用产品无实际经验&#xff0c;但是各种消息中间件大差不差&#xff0c;故而本次选择一个相对较熟悉的Kafka进行详细介绍…

应用机器学习的建议

一、决定下一步做什么 在你得到你的学习参数以后&#xff0c;如果你要将你的假设函数放到一组新的房屋样本上进行测试&#xff0c;假如说你在预测房价时产生了巨大的误差&#xff0c;你想改进这个算法&#xff0c;接下来应该怎么办&#xff1f;实际上你可以考虑先采用下面的几种…

深度强化学习(王树森)笔记03

深度强化学习&#xff08;DRL&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。本文在ChatGPT辅助下完成。 参考链接 Deep Reinforcement Learning官方链接&#xff1a;https://github.com/wangshusen/DRL 源代码链接&#xff1a;https://github.c…