List<T> 類與 ArrayList 類比較類似。它實現了 IList<T> 泛型接口,長度可以動態增加。
可以使用 Add 或 AddRange 方法將項添加到 List<T> 。
List<T> 類使用的相等比較器和排序比較器如下:
-
Contains、IndexOf、LastIndexOf、和 Remove 用於對列表中的元素進行相等比較。默認的相等比較器 T 按如下方式確定:如果類型 T 實現 IEquatable<T> 泛型接口,則相等比較器是 Equals(T) 方法該接口; 否則,默認的相等比較器是 Object.Equals(Object)。
-
BinarySearch 和 Sort 用於對列表元素進行排序。類型的默認比較器 T 按如下方式確定:如果類型 T 實現 IComparable<T> 泛型接口,則默認比較器是 CompareTo(T) 方法該接口; 否則為如果類型 T 實現非泛型 IComparable 接口,則默認比較器是 CompareTo(Object) 該接口的方法。 如果類型 T 實現兩個接口,則沒有默認比較器,並且必須顯式提供一個比較器或比較委托。
List<T> 不保證進行排序。 您必須對 List<T> 執行的操作之前進行排序 (如 BinarySearch) 。
可以使用整數索引訪問此集合中的元素。 在此集合中的索引是從零開始。
當元素個數非常多時,您可以在64位系統上,把配置信息中的 enabled 屬性設置為 true ,來獲取20億的容量。
List<T> 接受值為 null 的引用類型,並允許重復。
性能注意事項
在決定是否使用 List<T> 或 ArrayList 類,這兩個具有類似的功能,請記住, List<T> 類在大多數情況下更好地執行,並且是類型安全。 如果引用類型用於類型 T 的 List<T> 類,這兩個類的行為是完全相同。 但是,如果值類型用於類型 T, ,您需要考慮實現和裝箱問題。
如果值類型用於類型 T, ,編譯器將生成的實現 List<T> 專門針對該值類型的類。 這意味着一個列表元素的 List<T> 對象沒有進行裝箱就可以使用該元素,並且會創建大約 500 個列表元素后不對列表元素裝箱所節省的內存大於用來生成的類實現的內存。
請確保用於類型的值類型 T 實現 IEquatable<T> 泛型接口。 如果不是,方法如 Contains 必須調用 Object.Equals(Object) 框受影響的列表元素的方法。 如果值類型實現 IComparable 接口,並且您擁有源代碼,則還應實現 IComparable<T> 泛型接口,以防止 BinarySearch 和 Sort 中裝箱列表元素的方法。 如果您不擁有的源代碼,將傳遞 IComparer<T> 對象傳遞給 BinarySearch 和 Sort 方法
若要使用強類型集合或使用特定類型的集合,則優先選擇 List<T> 類,而不要選擇 ArrayList 類,也不要自己寫一個強類型的集合容器。是或自己編寫強類型包裝器集合。原因是一些功能 .NET 框架已經幫你實現,並且公共語言運行時可以共享 Microsoft 中間語言代碼和元數據,而這點您的代碼無法做到。
using System; using System.Collections.Generic; // 簡單的業務對象,會員Id用來區分不同的會員 // 會員名稱可以修改 public class Part : IEquatable<Part> { public string PartName { get; set; } public int PartId { get; set; } public override string ToString() { return "ID: " + PartId + " Name: " + PartName; } public override bool Equals(object obj) { if (obj == null) return false; Part objAsPart = obj as Part; if (objAsPart == null) return false; else return Equals(objAsPart); } public override int GetHashCode() { return PartId; } public bool Equals(Part other) { if (other == null) return false; return (this.PartId.Equals(other.PartId)); } // 還需要重載 == 合 != 運算符 // TODO.. } public class Example { public static void Main() { // 創建一個會員列表 List<Part> parts = new List<Part>(); // 向列表中添加會員 parts.Add(new Part() { PartName = "crank arm", PartId = 1234 }); parts.Add(new Part() { PartName = "chain ring", PartId = 1334 }); parts.Add(new Part() { PartName = "regular seat", PartId = 1434 }); parts.Add(new Part() { PartName = "banana seat", PartId = 1444 }); parts.Add(new Part() { PartName = "cassette", PartId = 1534 }); parts.Add(new Part() { PartName = "shift lever", PartId = 1634 }); // 輸出會員列表中的會員,這里將調用在Part類中重寫的ToString方法 Console.WriteLine(); foreach (Part aPart in parts) { Console.WriteLine(aPart); } // 檢查列表中是否存在id為1734的會員。這里調用Part類中重寫的IEquatable.Equals方法 // 它用來比較會員的id Console.WriteLine("\nContains(\"1734\"): {0}", parts.Contains(new Part { PartId = 1734, PartName = "" })); // 在第2個位置上添加一個會員 Console.WriteLine("\nInsert(2, \"1834\")"); parts.Insert(2, new Part() { PartName = "brake lever", PartId = 1834 }); //Console.WriteLine(); foreach (Part aPart in parts) { Console.WriteLine(aPart); } Console.WriteLine("\nParts[3]: {0}", parts[3]); Console.WriteLine("\nRemove(\"1534\")"); // 這里將移除id為1534、名稱為cogs的會員 // 雖然會員名稱不一樣,但是Equals方法只比較會員的id parts.Remove(new Part() { PartId = 1534, PartName = "cogs" }); Console.WriteLine(); foreach (Part aPart in parts) { Console.WriteLine(aPart); } Console.WriteLine("\nRemoveAt(3)"); //這里將移除排在第4位的會員 parts.RemoveAt(3); Console.WriteLine(); foreach (Part aPart in parts) { Console.WriteLine(aPart); } /* ID: 1234 Name: crank arm ID: 1334 Name: chain ring ID: 1434 Name: regular seat ID: 1444 Name: banana seat ID: 1534 Name: cassette ID: 1634 Name: shift lever Contains("1734"): False Insert(2, "1834") ID: 1234 Name: crank arm ID: 1334 Name: chain ring ID: 1834 Name: brake lever ID: 1434 Name: regular seat ID: 1444 Name: banana seat ID: 1534 Name: cassette ID: 1634 Name: shift lever Parts[3]: ID: 1434 Name: regular seat Remove("1534") ID: 1234 Name: crank arm ID: 1334 Name: chain ring ID: 1834 Name: brake lever ID: 1434 Name: regular seat ID: 1444 Name: banana seat ID: 1634 Name: shift lever RemoveAt(3) ID: 1234 Name: crank arm ID: 1334 Name: chain ring ID: 1834 Name: brake lever ID: 1444 Name: banana seat ID: 1634 Name: shift lever */ } }
常用屬性和方法
下面的示例演示List<T>類型常用的幾個屬性和方法:
using System; using System.Collections.Generic; namespace ConsoleApp { class Program { static void Main(string[] args) { //創建泛型List類實例 List<string> strList = new List<string>(); //向List類實例中添加數據 strList.Add("10"); strList.Add("11"); strList.Add("12"); strList.Add("13"); //使用匿名委托顯示字符串List的值 Action<string> Show = delegate (string strToShow) { Console.WriteLine("當前元素值{0}", strToShow); }; //在指定的位置插入元素 strList.Insert(2, "15"); //顯示每個元素的值 strList.ForEach(Show); //未壓縮前List的容量 Console.WriteLine("包含{0}個元素,可包含{1}個元素", strList.Count, strList.Capacity); //壓縮List strList.TrimExcess(); //壓縮后List的容量 Console.WriteLine("包含{0}個元素,可包含{1}個元素", strList.Count, strList.Capacity); //檢查List中是否包含指定的元素 Console.WriteLine("是否包含數據\"15\":{0}", strList.Contains("15")); //使用索引值訪問指定元素 Console.WriteLine("第2個元素:{0}", strList[2]); //從List中刪除指定元素 bool result = strList.Remove("105"); //創建List的匿名委托 Converter<string, int> converter = delegate (string strIn) { return int.Parse(strIn); }; //使用匿名委托顯示整型List的值 Action<int> print = delegate (int intToPrint) { Console.WriteLine("當前元素值:{0}", intToPrint); }; //使用匿名委托將字符串List類實例轉換為整型List類實例 List<int> iList = strList.ConvertAll<int>(converter); //顯示每個元素的值 iList.ForEach(print); Console.ReadLine(); /* 當前元素值10 當前元素值11 當前元素值15 當前元素值12 當前元素值13 包含5個元素,可包含8個元素 包含5個元素,可包含5個元素 是否包含數據"15":True 第3個元素:15 當前元素值10 當前元素值11 當前元素值15 當前元素值12 當前元素值13 */ } } }
線程安全
可以安全地執行多個讀取 List<T> 的操作,但如果在讀取時修改該集合,可能會出現問題。
若要確保線程安全,讀取過程中鎖定集合,或寫入操作。 若要啟用訪問集合以進行讀取和寫入的多個線程,則必須實現自己的同步。 具有內置同步的集合,請參閱中的類 System.Collections.Concurrent 命名空間。 本質上是線程 – 安全替代方法,請參閱 ImmutableList<T> 類。
參考資料:
1.《C# 編程兵書》第7章
2. https://msdn.microsoft.com/zh-cn/library/6sh2ey19(v=vs.110).aspx