数组(ArrayPool数组池、Span<T>结构)
前言
如果需要使用相同的类型的多个对象,就可以使用集合和数组,这一节主要讲解数组,其中会重点涉及到Span<T>结构和ArrayPool数组池。我们也会先涉及到简单的数组、多维数组、锯齿数组、Array类。
简单的数组、多维数组、锯齿数组
简单的数组介绍
数组的声明:
Int [] myArray;
初始化:
myArray=new int[];
还可以:
Int [] myArray=new int []{,,,};
访问数组:
myArray[];
多维数组介绍
一般的数组(也称一维数组)是用一个数字来索引,多维数组用两个或两个以上的数字进行索引。
声明多维数组时中间以,隔开,我们下面声明一个二维数组。
int [,] twodim=new int [,]
int[,] twodim = {
{ ,,},
{ ,,},
{ ,,}
};
一个三维数组。
int[,,] threedim = {
{ { ,},{ ,} },
{{ ,},{ ,} },
{ { ,},{ ,} }
};
Console.WriteLine(threedim[,,]);
锯齿数组
二维数组图形:

锯齿数组

在声明锯齿数组的时候要依次放置左右括号。在初始化锯齿数组时,只对第一对方括号中设置该数组包含的行数,定义各行中元素个数的第二个方括号设为空,因为这类数组的每一行包含不同的元素个数。
int[][] jagged = new int[][];
jagged[] = new int[] { , };
jagged[] = new int[] { , , , };
jagged[] = new int[] { , , };
Array类
创建数组:
Array intArray1 = Array.CreateInstance(typeof(int), );
for (int i = ; i < intArray1.Length; i++)
{
intArray1.SetValue(, i);
}
上面这段代码,描述了Array数组的创建以及设置值。CreateInstance()方法第一个参数为元素的类型,第二个参数为定义数组的大小。SetValue()方法设置值第一个参数为设置IDE值,第二个参数为设置的索引。
复制数组:
int[] intArray1 = { ,};
int[] intArray2 = (int[])intArray1.Clone();
因为数组是引用类型的,所以将一个数组的变量赋予另一个数组变量,就会得到两个引用同一个数组的变量,这是使用的是Clone()方法创建数组的浅表副本。使用Copy()方法也可以创建浅表副本,Clone()方法会创建一个数组,而Copy()方法必须传递阶数相同且有足够元素的已有数组。
排序:
class Program
{
static void Main(string[] args)
{
int[] list = { , , , , , , }; Console.Write("原始数组: ");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.WriteLine(); // 逆转数组
Array.Reverse(list);
Console.Write("逆转数组: ");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.WriteLine(); // 排序数组
Array.Sort(list);
Console.Write("排序数组: ");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.WriteLine();
} }
输出:
原始数组: 逆转数组: 排序数组:
在上述方法中Array.Sort()方法实现了数组的排序,而Array.Reverse()实现了数组的逆转 。
ArrayPool数组池
接下来重点来了,本文的重点一,ArrayPool数组池。如果一个应用需要创建和销毁许多的数组,垃圾收集器就要花费很多的功夫来做这些工作,为了较少垃圾收集器的工作,这里我们可以使用ArrayPool类来使用数组池。ArrayPool管理一个数组池,数组可以再这里租借内存,并且返回到这里。需要引用using System.Buffers;
创建数组池:
ArrayPool<int> arrayPool = ArrayPool<int>.Create(maxArrayLength:, maxArraysPerBucket: );
maxArrayLength的默认值是1024*10224字节(数组的长度),maxArraysPerBucket默认值是50(数组的数量)。
这里还可以使用以下方法来使用预定义的共享池。
ArrayPool<int> sharePool = ArrayPool<int>.Shared;
下面我们就一起看看如何去使用这个数组池吧:
class Program
{
static void Main(string[] args)
{
//定义数组池
ArrayPool<int> arrayPool = ArrayPool<int>.Create(maxArrayLength: , maxArraysPerBucket: ); //定义使用数组的长度
int arrayLenght = ;
int[] array = arrayPool.Rent(arrayLenght); //输出数组的长度
Console.WriteLine($"定义数组长度:{arrayLenght},实际数组长度:{array.Length}"); //对数组进行赋值
array[] = ;
array[] = ;
array[] = ;
array[] = ;
array[] = ; //输出数组的值
foreach (var item in array)
{
Console.WriteLine(item);
} //将内存返回给数组池,clearArray设置True清除数组,下次调用时为空,设置False,保留数组,下次调用还是现在的值
arrayPool.Return(array, clearArray: true);
foreach (var item in array)
{
Console.WriteLine(item);
} }
}
在上面事例中,我们使用Rent()方法请求池中的内存,Rent方法返回一个数组,其中至少包含所请求的元素个数。返回的数组可能会用到更多的内存,池中最少的请求为16个元素,紧接着是32,64,128以此类推。所以在上述例子中我们请求的长度为5,但是实际使用的元素个数为16个,多余的将根据类型对其赋值0或者null。
我们使用Return()方法将数组返回到池中,这里使用了一个可选参数clearArray,指定是否清除该数组,不清除的话下一个从池中租用这个数组的人可以读取到其中的数据。清除数据可以避免这种情况,但是会消耗更多的CPU时间。
Span<T>
Span<T>介绍
为了快速访问托管或非托管的连续内存,可以使用Spam<T>结构。一个可以使用Span<T>结构的例子就是数组,Span<T>结构在后台保存在连续的内存中,另一个例子就是长字符串。
使用Span<T>结构,可以直接访问数组元素。数组的元素没有复制,但是它们可以直接调用,并且比复制还快。
class Program
{
static void Main(string[] args)
{
int[] arr1 = { , , , , , , , , };
var span1 = new Span<int>(arr1);
span1[] = ;
Console.WriteLine(arr1[]);
}
}
输出:

这里将创建的arr1数组传递给Span<T>,同时Span<T>类型提供了一个索引器,这里直接修改span1的第二个值,然后再输出arr1数组中的第二个值,也是被其修改过得值。
Span<T>切片
Span<T>它一个强大的特性是,可以使用它访问数组的部分或者切片,使用切片的时候不会复制数组元素,他们是从Span中直接访问的。下面代码介绍了创建切片的两种方法:
class Program
{
static void Main(string[] args)
{
//定义简单的数组
int[] arr2 = { , , , , , , , , , , , , , }; //Span<T>对数组进行切片,访问arr2数组,从第三个开始,取长度6个的一个数组。
var span3 = new Span<int>(arr2, start: , length: ); //输出切片中的值
foreach (var item in span3)
{
Console.WriteLine(item);
}
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(""); //对span3进行切片处理,从第二个开始,去长度4个的一个数组
var span4 = span3.Slice(start: , length: ); foreach (var item in span4)
{
Console.WriteLine(item);
}
}
}
输出:

使用Span<T>改变值
前面介绍了如何使用Span<T>的索引器,更改数组的元素,下面介绍的将会有更多的选项,关于修改元素的值及复制。
class Program
{
static void Main(string[] args)
{
//定义简单的数组
int[] arr = { , , , , , , , , , , , , , }; //将数组传递给span
var span = new Span<int>(arr);
var span2 = new Span<int>(arr); //对span进行切片处理,从第四个开始
var span3 = span.Slice(start: ); //调用clear方法,用0填充span3
span3.Clear();
foreach (var item in span3)
{
Console.WriteLine("span3的值:"+item);
}
Console.WriteLine("span3的长度:"+span3.Length); //创建新的切片span4,从span2开始,长度3
Span<int> span4 = span2.Slice(start: , length: ); //调用Fill方法,用传入的值填充span4
span4.Fill(); foreach (var item in span4)
{
Console.WriteLine("span4的值"+item);
}
Console.WriteLine("span4的长度"+span4.Length); //将span4复制给span,复制失败
span4.CopyTo(span); //将span复制给span3 复制失败
if (!span.TryCopyTo(span3))
{
Console.WriteLine("复制不了");
}
}
}
输出:

上面事例中,显示调用clear()方法,该方法用0填充Span,然后调用了Fill()方法,该方法用传递给Fill方法的值来填充Span,同时也可以将一个Span<T>复制给另一个Span<T>,这里先是采用的CopyTo,在这个方法中,如果另一个目标span不够大,就会复制失败,这里可以使用TryCopyTo来优化此功能,如果目标不够大,将会返回false。以此来判断是否复制成功。上面例子中span4长度为3,而span长度为14,这里是复制成功了,然后其下面的操作,因为span3的长度是10,span复制给span3失败了。因为span3不够大。
上面事例中提供了改变值的一些方法,当我们不需要对值进行改变,只需要对数组进行读访问的时候,我们可以使用ReadOnlySpan<T>。这里定义了只读的Span
ReadOnlySpan<int> readonlySpan = new ReadOnlySpan<int>(arr);
总结
在本篇文章中,重点介绍了ArrayPool数组池和Span<T>结构,通过使用数组池,来降低数组创建和销毁时消耗的性能,减少垃圾回收器的工作,使用Span<T>可以快速的访问托管及非托管代码,创建切片来对数组和长字符串进行一定的操作。更加高效的操作数组。
青少年是一个美好而又是一去不可再得的时期,是将来一切光明和幸福的开端。——加里宁
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2n1gp5jweqww4
欢迎大家扫描下方二维码,和我一起学习更多的C#知识

数组(ArrayPool数组池、Span<T>结构)的更多相关文章
- ArrayPool数组池、Span<T>结构
数组(ArrayPool数组池.Span<T>结构) 目录 前言 简单的数组.多维数组.锯齿数组 Array类 ArrayPool数组池 Span Span介绍 Span切片 使用Span ...
- [Go] 复合类型(数组、切片、字典、结构体)变量的 初始化 及 注意事项
Go变量 初始化 对 复合类型(数组.切片.字典.结构体)变量的初始化是,有一些语法限制: 1.初始化表达式必须包含类型标签: 2.左花括号必须在类型尾部,不能另起一行: 3.多个成员初始值以逗号分隔 ...
- C语言定义结构体指针数组并初始化;里面全是结构体的地址
#include <stdio.h> #include <string.h> struct tells;//声明结构体 struct info { char *infos; } ...
- C/C++知识总结 五 复合数据类型 壹(数组、字符串与string、结构)
C/C++复合数据类型 壹(数组.结构) 数组 数组的意义.定义与创建 一.二维数组应用 字符数组与字符串处理函数 数组与指针---关系密切 数组的意义与定义创建 意义:反映数据间的特点(通过把同一类 ...
- C++中,指针数组和数组指针
这俩兄弟长得实在太像,以至于经常让人混淆.然而细心领会和甄别就会发现它们大有不同. 前者是指针数组,后者是指向数组的指针.更详细地说. 前: 指针数组;是一个元素全为指针的数组. 后: 数组指针;可以 ...
- c指针与数组,传参问题,指针数组与数组指针的区别,二维数组动态内存分配
一 数组的结构:顺序存储,看谭浩强中的图,牢记 1.数组名指代一种数据结构:数组 现在可以解释为什么第1个程序第6行的输出为10的问题,根据结论1,数组名str的内涵为一种数据结构,即一个长度为10的 ...
- c语言指针数组与数组指针
一.指针数组和数组指针的内存布局初学者总是分不出指针数组与数组指针的区别.其实很好理解:指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定.它是“储存指针的数组”的简称.数 ...
- [C++ Primer Plus] 第7章、函数(一)程序清单——递归,指针和const,指针数组和数组指针,函数和二维数组
程序清单7.6 #include<iostream> using namespace std; ; int sum_arr(int arr[], int n);//函数声明 void ma ...
- C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序
C#中缓存的使用 缓存的概念及优缺点在这里就不多做介绍,主要介绍一下使用的方法. 1.在ASP.NET中页面缓存的使用方法简单,只需要在aspx页的顶部加上一句声明即可: <%@ Outp ...
随机推荐
- 递归导致的StackOverflow的分析
递归在多层次遍历时尤为重要,这里我们不讲递归的实现,来谈谈递归的内存占用情况. 如下代码,当我们运行时很简单,StackOverflowException瞬间抛出:这里确实是“瞬间”出错了,线程堆栈溢 ...
- 统计插件,Highcharts,以及modelformset
一.modelfromset组件 1.作用:用于批量处理多个表单 form表单对应的组件是formset Modelform对应的组件是modelformset 2.引入 From django.fo ...
- serverless 项目配置及创建helloworld应用(二)
阅读目录 一:学习使用AWS Lambda来作为服务器引擎 二:使用serverless环境搭建 三:创建我们的第一个应用,hello world 服务 回到顶部 一:学习使用AWS Lambda来作 ...
- Zookeeper详解-概述(一)
ZooKeeper是一种分布式协调服务,用于管理大型主机.在分布式环境中协调和管理服务是一个复杂的过程.ZooKeeper通过其简单的架构和API解决了这个问题.ZooKeeper允许开发人员专注于核 ...
- URL收集
window下 php5.5 安装pthread扩展:http://blog.csdn.net/aoyoo111/article/details/19020161
- ABP之Setting
介绍 每个应用程序都需要存储一些设置,并在应用程序的某个地方使用这些设置.ABP提供了一个强大的基础设施来存储/检索在服务器端和客户端都可用的应用程序.租户和用户级别设置. 设置是通常存储在数据库(或 ...
- Java接口中的成员变量默认为(public、static、final)、方法为(public、abstract)
interface”(接口)可将其想象为一个“纯”抽象类.它允许创建者规定一个类的基本形式:方法名.自变量列表以及返回类型,但不实现方法主体.接口也可包含基本数据类型的数据成员,但它们都默认为publ ...
- JAVA获取项目工程下的文件
JAVA获取 工程下的文件 其实很简单主要是理解编译路径 ①class.getResource public static void main(String[] args) { WordTest ...
- Codeforces 777E:Hanoi Factory(贪心+栈)
http://codeforces.com/problemset/problem/777/E 题意:给出n个环状圆柱,每个圆环有一个内半径a,外半径b,和高度h,只有外半径bj <= bi并且b ...
- S7-1200 的运动控制
S7-1200 CPU本体集成点硬件输出点最高频率为100kHz,信号板上硬件集成点输出的最高频率为20kHz,CPU在使用PTO功能时将把集成点Qa.o,Qa.2或信号板的Q4.0作为脉冲输出点,Q ...