Generic的List和非Generic的ArrayList類支持可變化大小的對象數組,它們也是最多見的集合類。ArrayList實現了IList接口,而List<T>實現了IList<T>和IList接口(以及新增的IReadonlyList<T>)。與數組不一樣,全部的接口實現都是公開的,而且Add和Remove方法也對外公開;它們會按照你的但願執行。node
在List<T>和ArrayList內部,維護了一個內部的數組對象,當這個內部數組對象的大小超過期,建立一個新的數組來替代原數組。附加元素效率很高(由於一般有一個空閒插槽結尾),但插入的元素可能會很慢(由於有插入點以後的全部元素將位移以產生一個空閒插槽)。對於數組而言,若是集合若是是排好序的,那麼執行BinarySearch方法很是高效,但從另外一個方面而言,這又不高效,由於在執行BinarySearch以前,須要檢查每一個元素(以排序)。數組
對於值類型,List<T>的比ArrayList快幾倍,這是由於List<T>避免了裝箱和拆箱的開銷。安全
List<T>和ArrayList都提供了構造器方法,以接收元素集合;構造器方法遍歷集合的元素到新的List<T>或ArrayList對象中。List<T>的定義大體以下:數據結構
public class List<T> : IList<T>, IReadOnlyList<T> { public List (); public List (IEnumerable<T> collection); public List (int capacity); // Add+Insert public void Add (T item); public void AddRange (IEnumerable<T> collection); public void Insert (int index, T item); public void InsertRange (int index, IEnumerable<T> collection); // Remove public bool Remove (T item); public void RemoveAt (int index); public void RemoveRange (int index, int count); public int RemoveAll (Predicate<T> match); // Indexing public T this [int index] { get; set; } public List<T> GetRange (int index, int count); public Enumerator<T> GetEnumerator(); // Exporting, copying and converting: public T[] ToArray(); public void CopyTo (T[] array); public void CopyTo (T[] array, int arrayIndex); public void CopyTo (int index, T[] array, int arrayIndex, int count); public ReadOnlyCollection<T> AsReadOnly(); public List<TOutput> ConvertAll<TOutput> (Converter <T,TOutput> converter); // Other: public void Reverse(); // Reverses order of elements in list. public int Capacity { get;set; } // Forces expansion of internal array. public void TrimExcess(); // Trims internal array back to size. public void Clear(); // Removes all elements, so Count=0. }
除了上述方法以外,List<T>還提供了與Array類同樣的搜索和排序的實例方法。下面的例子演示了List的屬相和方法:多線程
static void Main(string[] args) { List<string> words = new List<string>(); words.Add("melon"); words.Add("avocado"); words.AddRange(new[] { "banana", "plum" }); words.Insert(0, "lemon"); words.InsertRange(0, new[] { "peach", "nashi" }); words.Remove("melon"); words.RemoveAt(3); words.RemoveRange(0, 2); words.RemoveAll(s => s.StartsWith("n")); Console.WriteLine(words[0]); Console.WriteLine(words[words.Count - 1]); foreach (string s in words) Console.WriteLine(s); string[] wordsArray = words.ToArray(); string[] existing = new string[1000]; words.CopyTo(0, existing, 998, 2); List<string> upperCastwords = words.ConvertAll(s => s.ToUpper()); List<int> lenghts = words.ConvertAll(s => s.Length); Console.ReadLine(); }
而非generic的ArrayList主要用於和Framework1.x的代碼兼容,由於其要求一個類型轉換,好比下面的代碼併發
ArrayList al = new ArrayList(); al.Add ("hello"); string first = (string) al [0]; string[] strArr = (string[]) al.ToArray (typeof (string));
而這樣的轉換不能被編譯器驗證,所以下面的代碼能夠經過編譯,可是在運行時卻會報錯less
int first = (int) al [0];
ArrayList和List<Object>相似。二者在處理混合類型的集合時很是有用。有一種場景特別適合使用ArrayList,那麼就是處理反射時。函數
若是你引用了System.Linq命名空間,那麼你就使用LINQ的Cast方法把一個ArrayList轉換成一個Generic的List測試
ArrayList al = new ArrayList(); al.AddRange(new[] { 1, 5, 9 }); List<int> list = al.Cast<int>().ToList();
Cast和ToList方法時System.Linq.Enumerable類的擴展方法。Cast方法首先嚐試直接把ArrayList轉換成List,若是轉換不成功,那麼遍歷ArrayList,轉換每一個元素,並返回IEnumerable<T>。而ToList()方法則是直接調用List<T>的構造函數public List (IEnumerable<T> collection);從而實現IEnumerable<T>轉換成List<T>。ui
List<T>接收值爲null的引用類型;此外List<T>還容許重複的元素。
List<T>既適用相等性比較器,也使用排序比較器。當調用Contains, IndexOf, LastIndeoxOf, Remove等方式時,會使用相等性比較器。List<T>會使用T類型的默認相等比較器。若是T類型實現了IEquatable<T>接口,那麼調用該接口的Equals(T)方法;不然調用Object.Equals(Object)方法進行相等性比較。List<T>的BinarySearch、Sort方法使用排序比較器。同相等性比較器同樣,List<T>會使用T類型的默認排序比較器,若是T類型實現了IComparable<T>接口,那麼調用該接口的CompareTo(T)方法,不然使用非Generic接口IComparable的CompareTo(Object)方法。
List<T>的靜態成員是線程安全的,而實例成員不能確保類型安全。多線程讀取IList<T>是線程安全的,可是若是在讀的過程當中被修改了,那麼就會引起問題,好比下面的代碼:
class Program { static List<int> numbers = new List<int>(); static void Main(string[] args) { numbers.Add(0); Thread t1 = new Thread(GetNum); t1.Start(); Thread t2 = new Thread(SetNum); t2.Start(); Console.ReadLine(); } static void GetNum() { Console.WriteLine("t1->" + numbers[0]); // -> 0 Thread.Sleep(1000); Console.WriteLine("t1->" + numbers[0]); // -> 2 } static void SetNum() { numbers[0] = 2; Console.WriteLine("t2->" + numbers[0]); // ->2 } }
在GetNum方法中,兩次讀取List<Int>的第一個元素時,值發生變化。所以,咱們須要手動實現線程同步。通常經常使用的方式時使用lock鎖住List<T>對象
class Program { static List<int> numbers = new List<int>(); static object locker = new object(); static void Main(string[] args) { numbers.Add(0); Thread t1 = new Thread(GetNum); t1.Start(); Thread t2 = new Thread(SetNum); t2.Start(); Console.ReadLine(); } static void GetNum() { lock (locker) { Console.WriteLine("t1->" + numbers[0]); // -> Thread.Sleep(1000); Console.WriteLine("t1->" + numbers[0]); // -> 0 } } static void SetNum() { lock (locker) { numbers[0] = 2; Console.WriteLine("t2->" + numbers[0]); // ->2 } } }
另外,微軟在System.Collection.Concurrent命名空間下,提供了幾個用於併發的集合類
LinkedList<T>是雙向鏈表列表。所謂雙向鏈表列表就是這樣節點鏈條,每一個節點都包含前一個節點引用,後一個節點引用,以及本身的引用。它最大的益處就是能夠高效地插入元素到列表的任意位置,由於它值須要建立一個新的節點,而後更新相關引用(前一個節點的引用和後一個節點的引用)。而後向鏈表列表的第一個位置插入新的節點可能會很慢,這是由於在鏈表內部沒有內在的索引機制,所以每次節點都須要遍歷,並且也不能使用二進制印章(binary-chop)搜索。下圖是LinkedList<T>示意圖
LinkedList<T>實現了IEnumerable<T>接口和ICollection<T>接口,但沒有實現IList<T>接口,因此其不支持索引。
LinkedListNode的代碼大體以下:
public sealed class LinkedListNode<T> { internal LinkedList<T> list; internal LinkedListNode<T> next; internal LinkedListNode<T> prev; internal T item; public LinkedListNode( T value) { this.item = value; } internal LinkedListNode(LinkedList<T> list, T value) { this.list = list; this.item = value; } public LinkedList<T> List { get { return list;} } public LinkedListNode<T> Next { get { return next == null || next == list.head? null: next;} } public LinkedListNode<T> Previous { get { return prev == null || this == list.head? null: prev;} } public T Value { get { return item;} set { item = value;} } internal void Invalidate() { list = null; next = null; prev = null; } }
當向LinkedList<T>添加一個節點時,你能夠致命節點的位置,或者相對於另外一個節點的位置,或者列表的開始/結束位置。LinkedList<T>提供了下面的方法添加節點
public void AddFirst(LinkedListNode<T> node); public LinkedListNode<T> AddFirst (T value); public void AddLast (LinkedListNode<T> node); public LinkedListNode<T> AddLast (T value); public void AddAfter (LinkedListNode<T> node, LinkedListNode<T> newNode); public LinkedListNode<T> AddAfter (LinkedListNode<T> node, T value); public void AddBefore (LinkedListNode<T> node, LinkedListNode<T> newNode); public LinkedListNode<T> AddBefore (LinkedListNode<T> node, T value);
與之對應,提供了刪除節點的方法
public void Clear(); public void RemoveFirst(); public void RemoveLast(); public bool Remove (T value); public void Remove (LinkedListNode<T> node);
LinkedList<T>內部有字段用於追蹤列表中元素的數量,首節點和尾節點:
public int Count { get; } public LinkedListNode<T> First { get; } public LinkedListNode<T> Last { get; }
LinkedList還支持下面的搜索方法
public bool Contains (T value); public LinkedListNode<T> Find (T value); public LinkedListNode<T> FindLast (T value);
最後,LinkedList<T>支持支持從數組複製元素,並提供了列舉器以支持foreach語法
public void CopyTo (T[] array, int index); public Enumerator<T> GetEnumerator();
下面的示例演示了若是使用LinkedList<T>
static void Main(string[] args) { var tune = new LinkedList<string>(); tune.AddFirst("do"); // do tune.AddLast("so"); // do - so tune.AddAfter(tune.First, "re"); // do - re- so tune.AddAfter(tune.First.Next, "mi"); // do - re - mi- so tune.AddBefore(tune.Last, "fa"); // do - re - mi - fa- so tune.RemoveFirst(); // re - mi - fa - so tune.RemoveLast(); // re - mi - fa LinkedListNode<string> miNode = tune.Find("mi"); tune.Remove(miNode); // re - fa tune.AddFirst(miNode); // mi- re - fa foreach (string s in tune) Console.WriteLine(s); Console.ReadLine(); }
Queue<T>和Queue是先進先出(FIFO)的數據結構,經過Enqueue和Dequeue方法實現添加元素到隊列的尾部和從隊列的頭部移除元素。Peek方法從隊列的頭部獲取一個元素(不移除該元素),Count屬性用來統計隊列中元素的個數(在執行出列操做時檢查元素是否存在很是有用)。
儘管隊列是可遍歷的,可是它們並無實現IList<T>接口和IList接口,所以也不能經過索引來訪問隊列中的元素。雖然也提供了ToArray方法,可是在複製元素時是從隊列中隨機讀取的。
Queue的定義大體以下:
public class Queue<T> : IEnumerable<T>, ICollection, IEnumerable { public Queue(); public Queue (IEnumerable<T> collection); // Copies existing elements public Queue (int capacity); // To lessen auto-resizing public void Clear(); public bool Contains (T item); public void CopyTo (T[] array, int arrayIndex); public int Count { get; } public T Dequeue(); public void Enqueue (T item); public Enumerator<T> GetEnumerator(); // To support foreach public T Peek(); public T[] ToArray(); public void TrimExcess(); }
下面的示例演示瞭如何使用Queue<T>
var q = new Queue<int>(); q.Enqueue (10); q.Enqueue (20); int[] data = q.ToArray(); // Exports to an array Console.WriteLine (q.Count); // "2" Console.WriteLine (q.Peek()); // "10" Console.WriteLine (q.Dequeue()); // "10" Console.WriteLine (q.Dequeue()); // "20" Console.WriteLine (q.Dequeue()); // throws an exception (queue empty)
Queue內部使用一個按需更改大小的數組來實現,這與List<T>相似。Queue使用索引指向隊列的頭部和尾部元素;所以,入列和出列操做都很是快速。
Stack<T>和Stack則是後進先出的數據結構,經過Push和Pop法實現添加元素到隊列的頂部和從隊列的頂部移除元素。一樣也提供了Peek方法、Count屬性和ToArray方法。其定義大體以下:
public class Stack<T> : IEnumerable<T>, ICollection, IEnumerable { public Stack(); public Stack (IEnumerable<T> collection); // Copies existing elements public Stack (int capacity); // Lessens auto-resizing public void Clear(); public bool Contains (T item); public void CopyTo (T[] array, int arrayIndex); public int Count { get; } public Enumerator<T> GetEnumerator(); // To support foreach public T Peek(); public T Pop(); public void Push (T item); public T[] ToArray(); public void TrimExcess(); }
下面的示例演示瞭如何使用Queue<T>
var s = new Stack<int>(); s.Push (1); // Stack = 1 s.Push (2); // Stack = 1,2 s.Push (3); // Stack = 1,2,3 Console.WriteLine (s.Count); // Prints 3 Console.WriteLine (s.Peek()); // Prints 3, Stack = 1,2,3 Console.WriteLine (s.Pop()); // Prints 3, Stack = 1,2 Console.WriteLine (s.Pop()); // Prints 2, Stack = 1 Console.WriteLine (s.Pop()); // Prints 1, Stack = <empty> Console.WriteLine (s.Pop()); // throws exception
與Queue<T>和List<T>同樣,Stacks內部使用一個按需更改大小的數組來實現。
BitArray是一個都bool值組成的、大小可動態變化的集合。它比bool[]或List<bool>有更多的內存效率,這是由於每一個元素都值佔用一bit的內存空間,而bool類型則佔用1byte空間(1byte=8bit)。經過索引器能夠讀取和更改BitArray元素的值。
var bits = new BitArray(2); bits[1] = true;
BitArray提供了四個位運算(Add, Or, Xor和Not)。最後最後一個方法以外,其餘的三個方法接收另一個BitArray
bits.Xor (bits); // Bitwise exclusive-OR bits with itself Console.WriteLine (bits[1]); // False
這三個方法很簡單,下面的代碼展現瞭如何使用它們
static void Main(string[] args) { BitArray array1 = new BitArray(new[]{true, false, false, true, true}); BitArray array2 = new BitArray(new[] { true, false, true, false, false }); Console.WriteLine("--Or--"); foreach (bool b in array1.Or(array2)) Console.WriteLine(b); array1 = new BitArray(new[] { true, false, false, true, true }); array2 = new BitArray(new[] { true, false, true, false, false }); Console.WriteLine("\n--And--"); foreach (bool b in array1.And(array2)) Console.WriteLine(b); array1 = new BitArray(new[] { true, false, false, true, true }); array2 = new BitArray(new[] { true, false, true, false, false }); Console.WriteLine("\n--Xor--"); foreach (bool b in array1.Xor(array2)) Console.WriteLine(b); Console.ReadLine(); }
HashSet<T>在Framework3.5才新增的,而SortedSet<T>是Framework4.0新增的。二者都有下面的特性
SortedSet<T>的元素是排過序的,而HashSet<T>的元素未排序。
HashSet<T>經過一個存貯鍵值的哈希表來實現;SortedSet<T>是經過紅黑樹實現。
兩個集合都實現了ICollection<T>接口,由於都提供了Contains,Add和Remove方法;此外,還有一個移除元素的方法RemoveWhere。
下面的代碼演示了構建一個HashSet<Char>集合,而後測試Contains方法,最後經過遍歷輸出集合元素
var letters = new HashSet<char> ("the quick brown fox"); Console.WriteLine (letters.Contains ('t')); // true Console.WriteLine (letters.Contains ('j')); // false foreach (char c in letters) Console.Write (c); // the quickbrownfx
這兩個集合類還包含了下面的幾個有意思的方法:對集合進行操做,並修改原集合
public void UnionWith (IEnumerable<T> other); // Adds public void IntersectWith (IEnumerable<T> other); // Removes public void ExceptWith (IEnumerable<T> other); // Removes public void SymmetricExceptWith (IEnumerable<T> other); // Removes
而下面的方法簡單的查詢集合,所以它們不會更改原集合:
public bool IsSubsetOf (IEnumerable<T> other); public bool IsProperSubsetOf (IEnumerable<T> other); public bool IsSupersetOf (IEnumerable<T> other); public bool IsProperSupersetOf (IEnumerable<T> other); public bool Overlaps (IEnumerable<T> other); public bool SetEquals (IEnumerable<T> other);
UnionWith方法把第二個集合中的元素添加到原集合中(重複的元素自動排除)。而InsersectWith方法移除在兩個集合中都不存在的元素(是兩個集合取交集)。
var letters = new HashSet<char> ("the quick brown fox"); letters.IntersectWith ("aeiou"); foreach (char c in letters) Console.Write (c); // euio
而ExceptWith則表示從原集合中刪除指定的元素(兩個集合相減)。
var letters = new HashSet<char> ("the quick brown fox"); letters.ExceptWith ("aeiou"); foreach (char c in letters) Console.Write (c); // th qckbrwnfx
SymmetricExceptWith方法刪除既屬於集合A和集合B交集以外的那些元素
var letters = new HashSet<char> ("the quick brown fox"); letters.SymmetricExceptWith ("the lazy brown fox"); foreach (char c in letters) Console.Write (c); // quicklazy
請注意,HashSet<T>和SortedSet<T>都實現了IEnumerable<T>,所以你在使用它們的集合操做方法時,能夠把另外一個集合看成該集合操做的參數。好比,你在使用HashSet<T>的集合操做,能夠傳一個SortedSet<T>。
SortedSet<T>比HashSet<T>還多了下面的幾個方法
public virtual SortedSet<T> GetViewBetween (T lowerValue, T upperValue) public IEnumerable<T> Reverse() public T Min { get; } public T Max { get; }
SortSet<T>的構造函數還接收IComparer<T>參數。
下面的例子演示瞭如何使用SortedSet<T>:
var letters = new SortedSet<char> ("the quick brown fox"); foreach (char c in letters) Console.Write (c); // bcefhiknoqrtuwx
參考連接:
http://stackoverflow.com/questions/2980283/thread-safe-collections-in-net