前言
如果需要使用相同的類型的多個(gè)對(duì)象,就可以使用集合和數(shù)組,這一節(jié)主要講解數(shù)組,其中會(huì)重點(diǎn)涉及到Span<T>結(jié)構(gòu)和ArrayPool數(shù)組池。我們也會(huì)先涉及到
簡(jiǎn)單的數(shù)組、多維數(shù)組、鋸齒數(shù)組、Array類。
簡(jiǎn)單的數(shù)組、多維數(shù)組、鋸齒數(shù)組
簡(jiǎn)單的數(shù)組介紹
數(shù)組的聲明:
Int [] myArray;
初始化:
myArray=new int[4];
還可以:
Int [] myArray=new int []{1,2,3,4};
訪問(wèn)數(shù)組:
myArray[0];
?
多維數(shù)組介紹
一般的數(shù)組(也稱一維數(shù)組)是用一個(gè)數(shù)字來(lái)索引,多維數(shù)組用兩個(gè)或兩個(gè)以上的數(shù)字進(jìn)行索引。
聲明多維數(shù)組時(shí)中間以,隔開(kāi),我們下面聲明一個(gè)二維數(shù)組。
int [,] twodim=new int [3,3] int[,] twodim = { { 1,2,3}, { 4,5,6}, { 7,8,9} };
?
一個(gè)三維數(shù)組。
int[,,] threedim = { { { 1,2},{ 3,4} }, {{ 5,6},{ 7,8} }, { { 9,10},{ 11,12} }
}; Console.WriteLine(threedim[0,1,1]);
?
鋸齒數(shù)組
二維數(shù)組圖形:
1
2
3
4
5
6
7
8
9
?
?
?
?
鋸齒數(shù)組
1
2
3
4
5
6
7
8
9
?
?
?
?
?
在聲明鋸齒數(shù)組的時(shí)候要依次放置左右括號(hào)。在初始化鋸齒數(shù)組時(shí),只對(duì)第一對(duì)方括號(hào)中設(shè)置該數(shù)組包含的行數(shù),定義各行中元素個(gè)數(shù)的第二個(gè)方括號(hào)設(shè)為空,因?yàn)檫@類數(shù)組的每一行包含不同的元素個(gè)數(shù)。
int[][] jagged = new int[3][]; jagged[0] = new int[2] { 1, 2 }; jagged[1] =
new int[4] { 3, 4, 5, 6 }; jagged[2] = new int[3] { 7, 8, 9 };
?
Array類
創(chuàng)建數(shù)組:
Array intArray1 = Array.CreateInstance(typeof(int), 5); for (int i = 0; i <
intArray1.Length; i++) { intArray1.SetValue(33, i); }
上面這段代碼,描述了Array數(shù)組的創(chuàng)建以及設(shè)置值。CreateInstance()方法第一個(gè)參數(shù)為元素的類型,第二個(gè)參數(shù)為定義數(shù)組的大小。
SetValue()方法設(shè)置值第一個(gè)參數(shù)為設(shè)置IDE值,第二個(gè)參數(shù)為設(shè)置的索引。
復(fù)制數(shù)組:
int[] intArray1 = { 1,2}; int[] intArray2 = (int[])intArray1.Clone();
因?yàn)閿?shù)組是引用類型的,所以將一個(gè)數(shù)組的變量賦予另一個(gè)數(shù)組變量,就會(huì)得到兩個(gè)引用同一個(gè)數(shù)組的變量,這是使用的是Clone()方法創(chuàng)建數(shù)組的淺表副本。使用
Copy()方法也可以創(chuàng)建淺表副本,Clone()方法會(huì)創(chuàng)建一個(gè)數(shù)組,而Copy()方法必須傳遞階數(shù)相同且有足夠元素的已有數(shù)組。
排序:
class Program { static void Main(string[] args) { int[] list = { 34, 72, 13, 44
,25, 30, 10 }; Console.Write("原始數(shù)組: "); foreach (int i in list) {
Console.Write(i+ " "); } Console.WriteLine(); // 逆轉(zhuǎn)數(shù)組 Array.Reverse(list);
Console.Write("逆轉(zhuǎn)數(shù)組: "); foreach (int i in list) { Console.Write(i + " "); }
Console.WriteLine();// 排序數(shù)組 Array.Sort(list); Console.Write("排序數(shù)組: "); foreach
(int i in list) { Console.Write(i + " "); } Console.WriteLine(); } }
?
輸出:
原始數(shù)組: 34 72 13 44 25 30 10 逆轉(zhuǎn)數(shù)組: 10 30 25 44 13 72 34 排序數(shù)組: 10 13 25 30 34 44
72
在上述方法中Array.Sort()方法實(shí)現(xiàn)了數(shù)組的排序,而Array.Reverse()實(shí)現(xiàn)了數(shù)組的逆轉(zhuǎn) 。
ArrayPool數(shù)組池
接下來(lái)重點(diǎn)來(lái)了,本文的重點(diǎn)一,ArrayPool數(shù)組池。如果一個(gè)應(yīng)用需要?jiǎng)?chuàng)建和銷毀許多的數(shù)組,垃圾收集器就要花費(fèi)很多的功夫來(lái)做這些工作,為了較少垃圾收集器的工作,這里我們可以使用ArrayPool類來(lái)使用數(shù)組池。ArrayPool管理一個(gè)數(shù)組池,數(shù)組可以再這里租借內(nèi)存,并且返回到這里。需要引用
using?System.Buffers;
創(chuàng)建數(shù)組池:
ArrayPool<int> arrayPool = ArrayPool<int>.Create(maxArrayLength:40000,
maxArraysPerBucket:10);
maxArrayLength的默認(rèn)值是1024*10224字節(jié)(數(shù)組的長(zhǎng)度),maxArraysPerBucket默認(rèn)值是50(數(shù)組的數(shù)量)。
這里還可以使用以下方法來(lái)使用預(yù)定義的共享池。
ArrayPool<int> sharePool = ArrayPool<int>.Shared;
下面我們就一起看看如何去使用這個(gè)數(shù)組池吧:
?
class Program { static void Main(string[] args) { //定義數(shù)組池 ArrayPool<int>
arrayPool = ArrayPool<int>.Create(maxArrayLength: 10000, maxArraysPerBucket: 10
);//定義使用數(shù)組的長(zhǎng)度 int arrayLenght = 5; int[] array = arrayPool.Rent(arrayLenght); //
輸出數(shù)組的長(zhǎng)度 Console.WriteLine($"定義數(shù)組長(zhǎng)度:{arrayLenght},實(shí)際數(shù)組長(zhǎng)度:{array.Length}"); //
對(duì)數(shù)組進(jìn)行賦值 array[0] = 0; array[1] = 1; array[2] = 2; array[3] = 3; array[4] = 4; //
輸出數(shù)組的值 foreach (var item in array) { Console.WriteLine(item); } //
將內(nèi)存返回給數(shù)組池,clearArray設(shè)置True清除數(shù)組,下次調(diào)用時(shí)為空,設(shè)置False,保留數(shù)組,下次調(diào)用還是現(xiàn)在的值
arrayPool.Return(array, clearArray:true); foreach (var item in array) {
Console.WriteLine(item); } } }
?
在上面事例中,
我們使用Rent()方法請(qǐng)求池中的內(nèi)存,Rent方法返回一個(gè)數(shù)組,其中至少包含所請(qǐng)求的元素個(gè)數(shù)。返回的數(shù)組可能會(huì)用到更多的內(nèi)存,池中最少的請(qǐng)求為16個(gè)元素,緊接著是32,64,128以此類推。所以在上述例子中我們請(qǐng)求的長(zhǎng)度為5,但是實(shí)際使用的元素個(gè)數(shù)為16個(gè),多余的將根據(jù)類型對(duì)其賦值0或者null。
我們使用Return()方法將數(shù)組返回到池中,這里使用了一個(gè)可選參數(shù)clearArray,指定是否清除該數(shù)組,不清除的話下一個(gè)從池中租用這個(gè)數(shù)組的人可以讀取到其中的數(shù)據(jù)。清除數(shù)據(jù)可以避免這種情況,但是會(huì)消耗更多的CPU時(shí)間。
Span<T>
Span<T>介紹
為了快速訪問(wèn)托管或非托管的連續(xù)內(nèi)存,可以使用Spam<T>結(jié)構(gòu)。一個(gè)可以使用Span<T>結(jié)構(gòu)的例子就是數(shù)組,Span<T>結(jié)構(gòu)在后臺(tái)保存在連續(xù)的內(nèi)存中,另一個(gè)例子就是長(zhǎng)字符串。
使用Span<T>結(jié)構(gòu),可以直接訪問(wèn)數(shù)組元素。數(shù)組的元素沒(méi)有復(fù)制,但是它們可以直接調(diào)用,并且比復(fù)制還快。
class Program { static void Main(string[] args) { int[] arr1 = { 3, 5, 7, 9, 11
,13, 15, 18, 19 }; var span1 = new Span<int>(arr1); span1[1] = 11;
Console.WriteLine(arr1[1]); } }
輸出:
?
?
這里將創(chuàng)建的arr1數(shù)組傳遞給Span<T>,同時(shí)Span<T>類型提供了一個(gè)索引器,這里直接修改span1的第二個(gè)值,然后再輸出arr1數(shù)組中的第二個(gè)值,也是被其修改過(guò)得值。
Span<T>切片
Span<T>它一個(gè)強(qiáng)大的特性是,可以使用它訪問(wèn)數(shù)組的部分或者切片,使用切片的時(shí)候不會(huì)復(fù)制數(shù)組元素,他們是從Span中直接訪問(wèn)的。下面代碼介紹了創(chuàng)建切片的兩種方法:
class Program { static void Main(string[] args) { //定義簡(jiǎn)單的數(shù)組 int[] arr2 = { 3, 5
,7, 9, 11, 13, 15, 18, 19, 20, 30, 40, 50, 60 }; //
Span<T>對(duì)數(shù)組進(jìn)行切片,訪問(wèn)arr2數(shù)組,從第三個(gè)開(kāi)始,取長(zhǎng)度6個(gè)的一個(gè)數(shù)組。 var span3 = new Span<int>(arr2,
start:3, length: 6); //輸出切片中的值 foreach (var item in span3) {
Console.WriteLine(item); } Console.WriteLine(""); Console.WriteLine("");
Console.WriteLine(""); //對(duì)span3進(jìn)行切片處理,從第二個(gè)開(kāi)始,去長(zhǎng)度4個(gè)的一個(gè)數(shù)組 var span4 =
span3.Slice(start:2, length: 4); foreach (var item in span4) {
Console.WriteLine(item); } } }
?
輸出:
?
?
使用Span<T>改變值
前面介紹了如何使用Span<T>的索引器,更改數(shù)組的元素,下面介紹的將會(huì)有更多的選項(xiàng),關(guān)于修改元素的值及復(fù)制。
class Program { static void Main(string[] args) { //定義簡(jiǎn)單的數(shù)組 int[] arr = { 3, 5,
7, 9, 11, 13, 15, 18, 19, 20, 30, 40, 50, 60 }; //將數(shù)組傳遞給span var span = new
Span<int>(arr); var span2 = new Span<int>(arr); //對(duì)span進(jìn)行切片處理,從第四個(gè)開(kāi)始 var span3
= span.Slice(start:4 ); //調(diào)用clear方法,用0填充span3 span3.Clear(); foreach (var item
in span3) { Console.WriteLine("span3的值:"+item); } Console.WriteLine("span3的長(zhǎng)度:"+
span3.Length);//創(chuàng)建新的切片span4,從span2開(kāi)始,長(zhǎng)度3 Span<int> span4 = span2.Slice(start: 3
, length:3); //調(diào)用Fill方法,用傳入的值填充span4 span4.Fill(42); foreach (var item in
span4) { Console.WriteLine("span4的值"+item); } Console.WriteLine("span4的長(zhǎng)度"+
span4.Length);//將span4復(fù)制給span,復(fù)制失敗 span4.CopyTo(span); //將span復(fù)制給span3 復(fù)制失敗 if
(!span.TryCopyTo(span3)) { Console.WriteLine("復(fù)制不了"); } } }
?
輸出:
?
?
上面事例中,
顯示調(diào)用clear()方法,該方法用0填充Span,然后調(diào)用了Fill()方法,該方法用傳遞給Fill方法的值來(lái)填充Span,同時(shí)也可以將一個(gè)Span<T>復(fù)制給另一個(gè)Span<T>,這里先是采用的CopyTo,在這個(gè)方法中,如果另一個(gè)目標(biāo)span不夠大,就會(huì)復(fù)制失敗,這里可以使用TryCopyTo來(lái)優(yōu)化此功能,如果目標(biāo)不夠大,將會(huì)返回false。以此來(lái)判斷是否復(fù)制成功。上面例子中span4長(zhǎng)度為3,而span長(zhǎng)度為14,這里是復(fù)制成功了,然后其下面的操作,因?yàn)閟pan3的長(zhǎng)度是10,span復(fù)制給span3失敗了。因?yàn)閟pan3不夠大。
上面事例中提供了改變值的一些方法,當(dāng)我們不需要對(duì)值進(jìn)行改變,只需要對(duì)數(shù)組進(jìn)行讀訪問(wèn)的時(shí)候,我們可以使用ReadOnlySpan<T>。這里定義了只讀的Span
ReadOnlySpan<int> readonlySpan = new ReadOnlySpan<int>(arr);
?
總結(jié)
在本篇文章中,重點(diǎn)介紹了ArrayPool數(shù)組池和Span<T>結(jié)構(gòu),通過(guò)使用數(shù)組池,來(lái)降低數(shù)組創(chuàng)建和銷毀時(shí)消耗的性能,減少垃圾回收器的工作,使用Span<T>可以快速的訪問(wèn)托管及非托管代碼,創(chuàng)建切片來(lái)對(duì)數(shù)組和長(zhǎng)字符串進(jìn)行一定的操作。更加高效的操作數(shù)組。
?
青少年是一個(gè)美好而又是一去不可再得的時(shí)期,是將來(lái)一切光明和幸福的開(kāi)端。——加里寧
我的博客即將同步至騰訊云+社區(qū),邀請(qǐng)大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=2n1gp5jweqww4
?
?
?
c#基礎(chǔ)知識(shí)詳解系列
<https://www.cnblogs.com/hulizhong/p/11205119.html>
?
歡迎大家掃描下方二維碼,和我一起學(xué)習(xí)更多的C#知識(shí)
?
?
?
?
熱門工具 換一換
