c#中Array,ArrayList 與List 的區別、共性與轉換


本文內容來自我寫的開源電子書《WoW C#》,現在正在編寫中,可以去WOW-Csharp/學習路徑總結.md at master · sogeisetsu/WOW-Csharp (github.com)來查看編寫進度。預計2021年年底會完成編寫,2022年2月之前會完成所有的校對和轉制電子書工作,爭取能夠在2022年將此書上架亞馬遜。編寫此書的目的是因為目前.NET市場相對低迷,很多優秀的書都是基於.NET framework框架編寫的,與現在的.NET 6相差太大,正規的.NET 5學習教程現在幾乎只有MSDN,可是MSDN雖然准確優美但是太過瑣碎,沒有過閱讀開發文檔的同學容易一頭霧水,於是,我就編寫了基於.NET 5的《WoW C#》。本人水平有限,歡迎大家去本書的開源倉庫sogeisetsu/WOW-Csharp關注、批評、建議和指導。

Array,ArrayList and List<T>

Array、ArrayList和List都是從IList派生出來的,它們都實現了IEnumerable接口

從某種意義上來說,ArrayList和List屬於集合的范疇,因為他們都來自程序集System.Collections,但是因為它們都是儲存了多個變量的數據結構,並且都不是類似鍵值對的組合,並且沒有先進先出或者先進后出的機制,故而稱為數組。

我們一般稱呼Array,ArrayList and List<T>為數組。

Array

Array必須在定義且不初始化賦值的時候(不初始化的情況下聲明數組變量除外)必須定義數組最外側的長度。比如:

int[] vs = new int[10];
int[,] duoWei = new int[3, 4];
int[][] jiaoCuo = new int[3][]; // 該數組是由三個一維數組組成的

一維數組

定義

用類似於這種方式定義一個數組

int[] array = new int[5];

初始化賦值

用類似於這種方式初始化

int[] array1 = new int[] { 1, 3, 5, 7, 9 };

也可以進行隱式初始化

int[] array2 = { 1, 3, 5, 7, 9 };
string[] weekDays2 = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };

用類似於下面這種方式先聲明,再賦值

int[] array3;
array3 = new int[] { 1, 3, 5, 7, 9 };   // OK
//array3 = {1, 3, 5, 7, 9};   // Error

多維數組

數組可具有多個維度。多維數組的每個元素是聲明時的數組所屬類型的元素。比如說int[,]的每個元素都是int類型而不是int[]類型。換種說法就是多維數組不能算做“數組組成的數組”

定義

用類似下面這種方式聲明一個二維數組的長度

int[,] array = new int[4, 2];

初始化賦值

用類似於下面的方式初始化多維數組:

// Two-dimensional array.
int[,] array2D = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
// The same array with dimensions specified.
int[,] array2Da = new int[4, 2] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
// A similar array with string elements.
string[,] array2Db = new string[3, 2] { { "one", "two" }, { "three", "four" },
                                        { "five", "six" } };

// Three-dimensional array.
int[,,] array3D = new int[,,] { { { 1, 2, 3 }, { 4, 5, 6 } },
                                 { { 7, 8, 9 }, { 10, 11, 12 } } };

還可在不指定級別的情況下初始化數組

int[,] array4 = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };

不初始化的情況下聲明數組變量:

int[,] array5;
array5 = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };   // OK
//array5 = {{1,2}, {3,4}, {5,6}, {7,8}};   // Error

元素賦值和獲取元素

可以用類似於array[1,2]的方式來獲取數組的值和為數組賦值。

GetLength(0)可以獲取最外圍數組的長度,GetLength(1)可以獲得第二層數組的長度。以此類推。為一個二維數組duoWei循環賦值的方式如下:

Console.WriteLine("二維數組賦值");
for (int i = 0; i < duoWei.GetLength(0); i++)
{
    for (int j = 0; j < duoWei.GetLength(1); j++)
    {
        duoWei[i, j] = i + j;
    }
}

如何獲取二維數組中的元素個數呢?

int[,] array = new int[,] {{1,2,3},{4,5,6},{7,8,9}};//定義一個3行3列的二維數組
int row = array.Rank;//獲取維數,這里指行數
int col = array.GetLength(1);//獲取指定維度中的元素個數,這里也就是列數了。(0是第一維,1表示的是第二維)
int col = array.GetUpperBound(0)+1;//獲取指定維度的索引上限,在加上一個1就是總數,這里表示二維數組的行數
int num = array.Length;//獲取整個二維數組的長度,即所有元的個數

來源:C#中如何獲取一個二維數組的兩維長度,即行數和列數?以及多維數組各個維度的長度? - jack_Meng - 博客園 (cnblogs.com)

交錯數組

交錯數組是一個數組,其元素是數組,大小可能不同。 交錯數組有時稱為“數組的數組”。

交錯數組不初始化就聲明的方式如下:

int[][] ccf;
ccf = new int[3][];

交錯數組類似於python的多維數組,比較符合人類的直覺,一個交錯數組里面包含了多個數組。

定義

可以采用類似下面的方式來聲明一個交錯數組:

// 定義多維數組要求每個維度的長度都相同 下面定義交錯數組
int[][] jiaoCuo = new int[3][]; // 該數組是由三個一維數組組成的

上面聲明的數組是具有三個元素的一維數組,其中每個元素都是一維整數數組。

可使用初始化表達式通過值來填充數組元素,這種情況下不需要數組大小。 例如:

jaggedArray[0] = new int[] { 1, 3, 5, 7, 9 };
jaggedArray[1] = new int[] { 0, 2, 4, 6 };
jaggedArray[2] = new int[] { 11, 22 };

初始化賦值

可在聲明數組時將其初始化,如:

int[][] jaggedArray2 = new int[][]
{
new int[] { 1, 3, 5, 7, 9 },
new int[] { 0, 2, 4, 6 },
new int[] { 11, 22 }
};

獲取元素和單個賦值

可以用類似於jiaoCuo[1][1]來獲取單個元素的值,也可以用類似於jiaoCuo[1][1] = 2;來為單個元素賦值。

可以采取類似於下面的方式來進行循環賦值:

Console.WriteLine("交錯數組循環賦值");
// 先聲明交錯數組中每一個數組的長度
for (int i = 0; i < 3; i++)
{
    jiaoCuo[i] = new int[i + 1];
}
// 然后對交錯數組中的每一個元素賦值
for (int i = 0; i < jiaoCuo.Length; i++)
{
    Console.WriteLine($"交錯數組的第{i + 1}層");
    for (int j = 0; j < jiaoCuo[i].Length; j++)
    {
        jiaoCuo[i][j] = i + j;
        Console.WriteLine(jiaoCuo[i][j]);
    }
}

方法和屬性

像數組這種儲存多個變量的數據結構,最重要的就是增查刪改、獲取長度和數據類型轉換Array因為數組的特性,長度不可改變,所以增查刪改只能有查和改。

Array類型用用類似於下面的方式進行改操作:

vs[0] = 12; //一維數組
duoWei[1, 2] = 3; //多維數組
jiaoCuo[1][1] = 2; //交錯數組

Array類型用類似於下面的方式進行查操作:

int[] vs = new int[10];
vs[0] = 12;
Console.WriteLine(Array.IndexOf(vs, 12)); //0
Console.WriteLine(vs.Contains(12)); // True

獲取長度

可以用類似於下面這種方式來獲取:

Console.WriteLine(vs.Length);
Console.WriteLine(vs.Count());

交錯數組的Length是獲取所包含數組的個數,多維數組的Length是獲取數組的元素的總個數,多維數組GetLength(0)可以獲取最外圍數組的長度,GetLength(1)可以獲得第二層數組的長度。以此類推。

Array.ConvertAll() 數據類型轉換

可以用Array.ConvertAll<TInput,TOutput>(TInput[], Converter<TInput,TOutput>) 來進行數組類型的轉換。

參數如下:

  • array

    TInput[]

要轉換為目標類型的從零開始的一維 Array

用於將每個元素從一種類型轉換為另一種類型的 Converter


來源:[Array.ConvertAll(TInput], Converter) 方法 (System) | Microsoft Docs

demo如下:

double[] vs3 = Array.ConvertAll(vs, item => (double)item);

切片

默認狀態下只能對一維數組進行切片,或者通過交錯數組獲取的一維數組也可以進行切片。

切片的方式類似於vs[1..5],表示vs數組從1到5,左閉右開。^1表示-1,即最后一個元素。[^3..^1]表示倒數第三個元素到倒數第一個元素,左閉右開。

獲取單個元素和賦值

可以采用下面的方式來獲取單個元素和為單個元素單獨賦值:

// 一維數組
Console.WriteLine(vs[1]);
vs[1] = 2;
// 多維數組
Console.WriteLine(duoWei[1, 2]);
duoWei[1, 2] = 3;
// 交錯數組
Console.WriteLine(jiaoCuo[1][0]);
jiaoCuo[1][0] = 0;

Array.ForEach 循環

System.Array里面也有ForEach方法,這是用於Array的。

demo:

Array.ForEach(vs, item => Console.WriteLine(item));

ArrayList

定義

用類似於下面的三種方式中的任意一種來聲明ArrayList:

ArrayList() 初始化 ArrayList 類的新實例,該實例為空並且具有默認初始容量。
ArrayList(ICollection) 初始化 ArrayList 類的新實例,該類包含從指定集合復制的元素,並具有與復制的元素數相同的初始容量。
ArrayList(Int32) 初始化 ArrayList 類的新實例,該實例為空並且具有指定的初始容量。

可以將Arraylist看作是一種長度可以自由變換,可以包含不同數據類型元素的數組。

初始化賦值

可以采用類似於下面的方式來初始化賦值:

ArrayList arrayList1 = new ArrayList() { 12, 334, 3, true };

循環

循環可以用for和foreach。

foreach (var item in arrayList)
{
    Console.WriteLine(item);
}

方法和屬性

list<T>類似,但是沒有ConvertAll方法。ArrayList本身沒有ForEach方法,但是也可以用傳統的foreach方法(就像前面提到的ArrayList的循環那樣)。

具體的方法和屬性請查看List 部分的方法和屬性

List<T>

定義

用類似於下面的三種方式中的任意一種來聲明List<T>

List() 初始化 List 類的新實例,該實例為空並且具有默認初始容量。
List(IEnumerable) 初始化 List 類的新實例,該實例包含從指定集合復制的元素並且具有足夠的容量來容納所復制的元素。
List(Int32) 初始化 List 類的新實例,該實例為空並且具有指定的初始容量。

初始化

用類似於下面的方式在聲明時初始化:

List<string> listA = new List<string>() { "hello", " ", "wrold" };

循環

List<T>有一個名稱為ForEach的方法:

public void ForEach (Action<T> action);

該方法的本質是要對 List 的每個元素執行的 Action 委托。Action 的參數即為List<T>在循環過程中的每個元素。

demo如下:

// 聲明
List<string> listA = new List<string>() { "hello", " ", "wrold" };
// 循環
var i = 0;
listA.ForEach(item =>
              {
                  Console.WriteLine($"第{i + 1}個");
                  Console.WriteLine(item);
                  i++;
              });

方法和屬性

從獲取長度、增查刪改、數據類型轉換、切片和循環來解析。其中除了數據類型轉換和List<T>類型本身就擁有的ForEach方法外,都適用於ArrayList。

先聲明一個List<string>作為演示的基礎:

List<string> listA = new List<string>() { "hello", " ", "wrold" };

屬性 長度

Count屬性可以獲取長度

Console.WriteLine(listA.Count);

屬性 取值

Console.WriteLine(listA[0]);

即增加元素,可以用Add方法:

listA.Add("12");

IndexOf獲取所在位置,Contains獲取是否包含。

Console.WriteLine(listA.IndexOf("12"));
Console.WriteLine(listA.Contains("12"));

Remove根據數據刪除,RemoveAt根據位置刪除。

listA.Remove("12");
listA.RemoveAt(1);

可以用類似於listA[1] = "改變";的方式來修改元素內容。

切片

可以用GetRange(int index, int count)來進行切片操作,第一個參數是切片開始的位置,第二個參數是切片的數量,即從index開始往后數幾個數。

Console.WriteLine(listA.GetRange(1, 1).Count);

循環

List<T>有一個名稱為ForEach的方法,該方法的本質是要對 List 的每個元素執行的 Action 委托。Action 的參數即為List<T>在循環過程中的每個元素。

demo如下:

// 聲明
List<string> listA = new List<string>() { "hello", " ", "wrold" };
// 循環
var i = 0;
listA.ForEach(item =>
              {
                  Console.WriteLine($"第{i + 1}個");
                  Console.WriteLine(item);
                  i++;
              });

數據類型轉換

可以用ConvertAll來對數組的數據類型進行轉換,這是List<T>自帶的方法。System.Array里面也有ConvertAll方法,這是用於Array的。

List<object> listObject = listA.ConvertAll(s => (object)s);

區別

成員單一類型 長度可變 切片友好 方法豐富 增查刪改 ConvertAll
一維數組 查、改
多維數組 查、改
交錯數組 查、改
ArrayList 增查刪改
List<T> 增查刪改

Array最大的好處就是切片友好,可以使用類似於[1..3]的方式切片,這是比GetRange更加直觀的切片方式。List<T>類型可以通過ToArray的方法來轉變成Array。

Array,ArrayList and List<T>之間的轉換

關於這一部分的demo代碼詳情可從Array,ArrayList and List之間的轉換 · sogeisetsu/Solution1@88f27d6 (github.com)獲得。

先分別聲明這三種數據類型。

// 聲明數組
int[] a = new int[] { 1,3,4,5,656,-1 };
// 聲明多維數組
int[,] aD = new int[,] { { 1, 2 }, { 3, 4 } };
// 聲明交錯數組
int[][] aJ = new int[][] {
    new int[]{ 1,2,3},
    new int[]{ 1}
};
// 聲明ArrayList
ArrayList b = new ArrayList() { 1, 2, 344, "233", true };
// 聲明List<T>
List<int> c = new List<int>();

Array轉ArrayList

// 數組轉ArrayList
ArrayList aToArrayList = new ArrayList(a);

Array轉List<T>

List<int> aToList = new List<int>(a);
List<int> aToLista = a.ToList();

List<T>轉Array

int[] cToList = c.ToArray();

List<T>轉ArrayList

ArrayList cToArrayList = new ArrayList(c);

ArrayList轉Array

在轉換的過程中,會丟失數據類型的准確度,簡單來說就是轉換成的Array會變成object

// ArrayList轉Array
object[] bToArray = b.ToArray();

這種轉換的意義不大,如果轉換完之后再強行用Array.ConvertAll方法來進行數據類型的轉換,很有可能會出現諸如Unable to cast object of type 'System.String' to type 'System.Int32'.的錯誤,這是因為ArrayList本身成員就可以不是單一類型。

數組的打印

Array的打印

對於Array的打印,我找到了四種方式,如下:

  • 調用Array.ForEach

    Array.ForEach(a, item => Console.WriteLine(item));
    
  • 傳統forEach

    foreach (var item in a)
    {
    Console.WriteLine(item);
    }
    
  • 傳統for

    for (int i = 0; i < a.Count(); i++)
    {
    Console.WriteLine(a[i]);
    }
    
  • string.Join

    Console.WriteLine(string.Join("\t", a));
    

ArrayList的打印

ArrayList的打印我知道的就只有傳統的for和foreach兩種方式。

List<T>的打印

List<T>的打印除了傳統的for和foreach兩種方式之外,還有List<T>本身自帶的foreach:

var i = 0;
listA.ForEach(item =>
              {
                  Console.WriteLine($"第{i + 1}個");
                  Console.WriteLine(item);
                  i++;
              });

請注意:ArrayList和List<T>均沒有string.Join和調用Array.ForEach兩種方式來打印數組。

LICENSE

已將所有引用其他文章之內容清楚明白地標注,其他部分皆為作者勞動成果。對作者勞動成果做以下聲明:

copyright © 2021 蘇月晟,版權所有。

知識共享許可協議
作品蘇月晟采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM