数组(ArrayPool数组池、Span<T>结构)


前言

  如果需要使用相同的类型的多个对象,就可以使用集合和数组,这一节主要讲解数组,其中会重点涉及到Span<T>结构和ArrayPool数组池。我们也会先涉及到简单的数组、多维数组、锯齿数组、Array类。

简单的数组、多维数组、锯齿数组

  简单的数组介绍

  数组的声明:

  1. Int [] myArray;

  初始化:

  1. myArray=new int[4];

  还可以:

  1. Int [] myArray=new int []{1,2,3,4};

  访问数组:

  1. myArray[0];

  多维数组介绍

  一般的数组(也称一维数组)是用一个数字来索引,多维数组用两个或两个以上的数字进行索引。

  声明多维数组时中间以,隔开,我们下面声明一个二维数组。

  1. int [,] twodim=new int [3,3]
  2. int[,] twodim = {
  3. { 1,2,3},
  4. { 4,5,6},
  5. { 7,8,9}
  6. };

  一个三维数组。

  1. int[,,] threedim = {
  2. { { 1,2},{ 3,4} },
  3. {{ 5,6},{ 7,8} },
  4. { { 9,10},{ 11,12} }
  5. };
  6. Console.WriteLine(threedim[0,1,1]);

  锯齿数组

  二维数组图形:

  锯齿数组

  在声明锯齿数组的时候要依次放置左右括号。在初始化锯齿数组时,只对第一对方括号中设置该数组包含的行数,定义各行中元素个数的第二个方括号设为空,因为这类数组的每一行包含不同的元素个数。

  1.    int[][] jagged = new int[3][];
  2. jagged[0] = new int[2] { 1, 2 };
  3. jagged[1] = new int[4] { 3, 4, 5, 6 };
  4. jagged[2] = new int[3] { 7, 8, 9 };

Array类

  创建数组:

  1. Array intArray1 = Array.CreateInstance(typeof(int), 5);
  2. for (int i = 0; i < intArray1.Length; i++)
  3. {
  4. intArray1.SetValue(33, i);
  5.   }

  上面这段代码,描述了Array数组的创建以及设置值。CreateInstance()方法第一个参数为元素的类型,第二个参数为定义数组的大小。SetValue()方法设置值第一个参数为设置IDE值,第二个参数为设置的索引。

  复制数组:

  1. int[] intArray1 = { 1,2};
  2. int[] intArray2 = (int[])intArray1.Clone();

  因为数组是引用类型的,所以将一个数组的变量赋予另一个数组变量,就会得到两个引用同一个数组的变量,这是使用的是Clone()方法创建数组的浅表副本。使用Copy()方法也可以创建浅表副本,Clone()方法会创建一个数组,而Copy()方法必须传递阶数相同且有足够元素的已有数组。

  排序:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. int[] list = { 34, 72, 13, 44, 25, 30, 10 };
  6.  
  7. Console.Write("原始数组: ");
  8. foreach (int i in list)
  9. {
  10. Console.Write(i + " ");
  11. }
  12. Console.WriteLine();
  13.  
  14. // 逆转数组
  15. Array.Reverse(list);
  16. Console.Write("逆转数组: ");
  17. foreach (int i in list)
  18. {
  19. Console.Write(i + " ");
  20. }
  21. Console.WriteLine();
  22.  
  23. // 排序数组
  24. Array.Sort(list);
  25. Console.Write("排序数组: ");
  26. foreach (int i in list)
  27. {
  28. Console.Write(i + " ");
  29. }
  30. Console.WriteLine();
  31. }
  32.  
  33. }

  输出:

  1. 原始数组: 34 72 13 44 25 30 10
  2.  
  3. 逆转数组: 10 30 25 44 13 72 34
  4.  
  5. 排序数组: 10 13 25 30 34 44 72

  在上述方法中Array.Sort()方法实现了数组的排序,而Array.Reverse()实现了数组的逆转 。

ArrayPool数组池

  接下来重点来了,本文的重点一,ArrayPool数组池。如果一个应用需要创建和销毁许多的数组,垃圾收集器就要花费很多的功夫来做这些工作,为了较少垃圾收集器的工作,这里我们可以使用ArrayPool类来使用数组池。ArrayPool管理一个数组池,数组可以再这里租借内存,并且返回到这里。需要引用using System.Buffers;

  创建数组池:

  1. ArrayPool<int> arrayPool = ArrayPool<int>.Create(maxArrayLength:40000, maxArraysPerBucket: 10);

  maxArrayLength的默认值是1024*10224字节(数组的长度),maxArraysPerBucket默认值是50(数组的数量)。

  这里还可以使用以下方法来使用预定义的共享池。

  1. ArrayPool<int> sharePool = ArrayPool<int>.Shared;

  下面我们就一起看看如何去使用这个数组池吧:

 

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. //定义数组池
  6. ArrayPool<int> arrayPool = ArrayPool<int>.Create(maxArrayLength: 10000, maxArraysPerBucket: 10);
  7.  
  8. //定义使用数组的长度
  9. int arrayLenght = 5;
  10. int[] array = arrayPool.Rent(arrayLenght);
  11.  
  12. //输出数组的长度
  13. Console.WriteLine($"定义数组长度:{arrayLenght},实际数组长度:{array.Length}");
  14.  
  15. //对数组进行赋值
  16. array[0] = 0;
  17. array[1] = 1;
  18. array[2] = 2;
  19. array[3] = 3;
  20. array[4] = 4;
  21.  
  22. //输出数组的值
  23. foreach (var item in array)
  24. {
  25. Console.WriteLine(item);
  26. }
  27.  
  28. //将内存返回给数组池,clearArray设置True清除数组,下次调用时为空,设置False,保留数组,下次调用还是现在的值
  29. arrayPool.Return(array, clearArray: true);
  30. foreach (var item in array)
  31. {
  32. Console.WriteLine(item);
  33. }
  34.  
  35. }
  36. }

  在上面事例中,我们使用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>结构,可以直接访问数组元素。数组的元素没有复制,但是它们可以直接调用,并且比复制还快。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. int[] arr1 = { 3, 5, 7, 9, 11, 13, 15, 18, 19 };
  6. var span1 = new Span<int>(arr1);
  7. span1[1] = 11;
  8. Console.WriteLine(arr1[1]);
  9. }
  10. }

  输出:

  这里将创建的arr1数组传递给Span<T>,同时Span<T>类型提供了一个索引器,这里直接修改span1的第二个值,然后再输出arr1数组中的第二个值,也是被其修改过得值。

  Span<T>切片

  Span<T>它一个强大的特性是,可以使用它访问数组的部分或者切片,使用切片的时候不会复制数组元素,他们是从Span中直接访问的。下面代码介绍了创建切片的两种方法:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. //定义简单的数组
  6. int[] arr2 = { 3, 5, 7, 9, 11, 13, 15, 18, 19, 20, 30, 40, 50, 60 };
  7.  
  8. //Span<T>对数组进行切片,访问arr2数组,从第三个开始,取长度6个的一个数组。
  9. var span3 = new Span<int>(arr2, start: 3, length: 6);
  10.  
  11. //输出切片中的值
  12. foreach (var item in span3)
  13. {
  14. Console.WriteLine(item);
  15. }
  16. Console.WriteLine("");
  17. Console.WriteLine("");
  18. Console.WriteLine("");
  19.  
  20. //对span3进行切片处理,从第二个开始,去长度4个的一个数组
  21. var span4 = span3.Slice(start: 2, length: 4);
  22.  
  23. foreach (var item in span4)
  24. {
  25. Console.WriteLine(item);
  26. }
  27. }
  28. }

  输出:

  使用Span<T>改变值

  前面介绍了如何使用Span<T>的索引器,更改数组的元素,下面介绍的将会有更多的选项,关于修改元素的值及复制。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. //定义简单的数组
  6. int[] arr = { 3, 5, 7, 9, 11, 13, 15, 18, 19, 20, 30, 40, 50, 60 };
  7.  
  8. //将数组传递给span
  9. var span = new Span<int>(arr);
  10. var span2 = new Span<int>(arr);
  11.  
  12. //对span进行切片处理,从第四个开始
  13. var span3 = span.Slice(start: 4 );
  14.  
  15. //调用clear方法,用0填充span3
  16. span3.Clear();
  17. foreach (var item in span3)
  18. {
  19. Console.WriteLine("span3的值:"+item);
  20. }
  21. Console.WriteLine("span3的长度:"+span3.Length);
  22.  
  23. //创建新的切片span4,从span2开始,长度3
  24. Span<int> span4 = span2.Slice(start: 3, length: 3);
  25.  
  26. //调用Fill方法,用传入的值填充span4
  27. span4.Fill(42);
  28.  
  29. foreach (var item in span4)
  30. {
  31. Console.WriteLine("span4的值"+item);
  32. }
  33. Console.WriteLine("span4的长度"+span4.Length);
  34.  
  35. //将span4复制给span,复制失败
  36. span4.CopyTo(span);
  37.  
  38. //将span复制给span3 复制失败
  39. if (!span.TryCopyTo(span3))
  40. {
  41. Console.WriteLine("复制不了");
  42. }
  43. }
  44. }

  输出:

  上面事例中,显示调用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

  1. ReadOnlySpan<int> readonlySpan = new ReadOnlySpan<int>(arr);

总结

  在本篇文章中,重点介绍了ArrayPool数组池和Span<T>结构,通过使用数组池,来降低数组创建和销毁时消耗的性能,减少垃圾回收器的工作,使用Span<T>可以快速的访问托管及非托管代码,创建切片来对数组和长字符串进行一定的操作。更加高效的操作数组。

ArrayPool数组池、Span<T>结构的更多相关文章

  1. 数组(ArrayPool数组池、Span<T>结构)

    前言 如果需要使用相同的类型的多个对象,就可以使用集合和数组,这一节主要讲解数组,其中会重点涉及到Span<T>结构和ArrayPool数组池.我们也会先涉及到简单的数组.多维数组.锯齿数 ...

  2. 数据结构笔记--栈的总结及java数组实现简单栈结构

    杂谈"栈"结构: 栈(Stack)是一种插入删除操作都只能在一个位置上进表,这个位置位于表的末端,叫做栈顶(Top). 对栈的基本操作有push和pop,表示进栈和出栈.也就相当于 ...

  3. 数组实现栈的结构(java)

    自定义数组实现栈的结构. package test; public class MyArrayStackClient { public static void main(String[] args) ...

  4. js List<Map> 将偏平化的数组转为树状结构并排序

    数据格式: [ { "id":"d3e8a9d6-e4c6-4dd8-a94f-07733d3c1b59", "parentId":&quo ...

  5. JAVA之旅(三)——数组,堆栈内存结构,静态初始化,遍历,最值,选择/冒泡排序,二维数组,面向对象思想

    JAVA之旅(三)--数组,堆栈内存结构,静态初始化,遍历,最值,选择/冒泡排序,二维数组,面向对象思想 我们继续JAVA之旅 一.数组 1.概念 数组就是同一种类型数据的集合,就是一个容器 数组的好 ...

  6. NumPy-快速处理数据--ndarray对象--多维数组的存取、结构体数组存取、内存对齐、Numpy内存结构

    本文摘自<用Python做科学计算>,版权归原作者所有. 上一篇讲到:NumPy-快速处理数据--ndarray对象--数组的创建和存取 接下来接着介绍多维数组的存取.结构体数组存取.内存 ...

  7. C语言实现使用动态数组来构造栈结构

    我在面前一篇博客<C语言实现使用静态数组来构造栈结构>中使用了静态数组来模拟栈的操作.静态数组的大小是在代码中写死的.是存储在用户栈上面的,使用起来不灵活.在这篇博客中我会使用动态数组来构 ...

  8. 用C语言关于学生管理系统的几种实现方法(一位数组,二维数组,指针,结构体)

    一位数组: #include <stdio.h> #include<string.h> #define N 5 void luru(float s[],int n); void ...

  9. Swift - 复杂数据类型说明(数组,字典,结构体,枚举)

    1,数组 - Array 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 var types ...

随机推荐

  1. 项目Alpha冲刺--8/10

    项目Alpha冲刺--8/10 作业要求 这个作业属于哪个课程 软件工程1916-W(福州大学) 这个作业要求在哪里 项目Alpha冲刺 团队名称 基于云的胜利冲锋队 项目名称 云评:高校学生成绩综合 ...

  2. 项目Alpha冲刺 9

    作业描述 课程: 软件工程1916|W(福州大学) 作业要求: 项目Alpha冲刺(团队) 团队名称: 火鸡堂 作业目标: 介绍第9天冲刺的项目进展.问题困难和心得体会 1.团队信息 队名:火鸡堂 队 ...

  3. 转成p进制算法C语言

    今天打比赛的时候竟然下一没有想起来, 实际上是非常简单的. 举例说明: $64 = 2 \times 3^3 + 1 \times 3^2 + 3^0$ 根据秦九韶算法每次提出3,即 $3(2 \ti ...

  4. PostgreSQL 一些比较好用的字符串函数

    最近刚接触到PostgreSQL数据库,发现很多功能比较强大的内置函数,特此记录下来.示例下次再补. 1.concat 字符串连接函数 2.concat_ws concat_ws函数连接可自定义分隔符 ...

  5. 对生成对抗网络GANs原理、实现过程、应用场景的理解(附代码),另附:深度学习大神文章列表

    https://blog.csdn.net/love666666shen/article/details/75522489 https://blog.csdn.net/yangdelong/artic ...

  6. JavaScript开发——文件夹的上传和下载

    我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用. 首先我们需要了解的是上传文件三要素: 1.表单提交方式:post (get方式提交有大小 ...

  7. 75: libreoj #10028 双向宽搜

    $des$ 实现一个bfs $sol$ 写了一个双向bfs #include <bits/stdc++.h> using namespace std; #define Rep(i, a, ...

  8. git dev分支合并线上master

    1.本地dev新建/切换本地master 新建 git checkout -b master 切换 git checkout  master 2.本地dev与本地master合并 git merge ...

  9. java中&& 的运算优先级高于||

    public class First { public static void main(String[] args) { boolean a = false; boolean b = true; b ...

  10. tar加密码

    tar -zcvf - *** | openssl des3 -salt -k pass | dd of=.his dd if=.his | openssl des3 -d -k pass| tar ...