一. 各類數據結構比較及其線程安全問題
1. Array(數組):
分配在連續內存中,不能隨意擴展,數組中數值類型必須是一致的。數組的聲明有兩種形式:直接定義長度,然后賦值;直接賦值。
缺點:插入數據慢。
優點:性能高,數據再多性能也沒有影響
特別注意:Array不是線程安全,在多線程中需要配合鎖機制來進行,如果不想使用鎖,可以用ConcurrentStack這個線程安全的數組來替代Array。
1 { 2 Console.WriteLine("---------------------------01 Array(數組)-----------------------------------"); 3 //模式一:聲明數組並指定長度 4 int[] array = new int[3]; 5 //數組的賦值通過下標來賦值 6 for (int i = 0; i < array.Length; i++) 7 { 8 array[i] = i + 10; 9 } 10 //數組的修改通過下標來修改 11 array[2] = 100; 12 //輸出 13 for (int j = 0; j < array.Length; j++) 14 { 15 Console.WriteLine(array[j]); 16 } 17 18 //模式二:直接賦值 19 string[] array2 = new string[] { "二胖", "二狗" }; 20 }
2. ArrayList(可變長度的數組)
不必在聲明的時候指定長度,即長度可變;可以存放不同的類型的元素。
致命缺點:無論什么類型存到ArrayList中都變為object類型,使用的時候又被還原成原先的類型,所以它是類型不安全的,當值類型存入的時候,會發生裝箱操作,變為object引用類型,而使用的時候,又將object類型拆箱,變為原先的值類型,這尼瑪,你能忍?
結論:不推薦使用,建議使用List代替!!
特別注意:ArrayList不是線程安全,在多線程中需要配合鎖機制來進行。
1 { 2 Console.WriteLine("---------------------------02 ArrayList(可變長度的數組)-----------------------------------"); 3 ArrayList arrayList = new ArrayList(); 4 arrayList.Add("二胖"); 5 arrayList.Add("馬茹"); 6 arrayList.Add(100); 7 for (int i = 0; i < arrayList.Count; i++) 8 { 9 Console.WriteLine(arrayList[i] + "類型為:" + arrayList[i].GetType()); 10 } 11 }
3. List<T> (泛型集合) 推薦使用
內部采用array實現,但沒有拆箱和裝箱的風險,是類型安全的
特別注意:List<T>不是線程安全,在多線程中需要配合鎖機制來進行,如果不想使用鎖,可以用ConcurrentBag這個線程安全的數組來替代List<T>
1 { 2 Console.WriteLine("---------------------------03 List<T> (泛型集合)-----------------------------------"); 3 List<string> arrayList = new List<string>(); 4 arrayList.Add("二胖"); 5 arrayList.Add("馬茹"); 6 arrayList.Add("大胖"); 7 //修改操作 8 arrayList[2] = "葛帥"; 9 //刪除操作 10 //arrayList.RemoveAt(0); 11 for (int i = 0; i < arrayList.Count; i++) 12 { 13 Console.WriteLine(arrayList[i]); 14 } 15 }
4. LinkedList<T> 鏈表
在內存空間中存儲的不一定是連續的,所以和數組最大的區別就是,無法用下標訪問。
優點:增加刪除快,適用於經常增減節點的情況。
缺點:無法用下標訪問,查詢慢,需要從頭挨個找。
特別注意:LinkedList<T>不是線程安全,在多線程中需要配合鎖機制來進行。
{ Console.WriteLine("---------------------------04 ListLink<T> 鏈表-----------------------------------"); LinkedList<string> linkedList = new LinkedList<string>(); linkedList.AddFirst("二胖"); linkedList.AddLast("馬茹"); var node1 = linkedList.Find("二胖"); linkedList.AddAfter(node1, "三胖"); //刪除操作 linkedList.Remove(node1); //查詢操作 foreach (var item in linkedList) { Console.WriteLine(item); } }
5. Queue<T> 隊列
先進先出,入隊(Enqueue)和出隊(Dequeue)兩個操作
特別注意:Queue<T>不是線程安全,在多線程中需要配合鎖機制來進行,如果不想使用鎖,線程安全的隊列為 ConcurrentQueue。
實際應用場景:利用隊列解決高並發問題(詳見:http://www.cnblogs.com/yaopengfei/p/8322016.html)
1 { 2 Console.WriteLine("---------------------------05 Queue<T> 隊列-----------------------------------"); 3 Queue<int> quereList = new Queue<int>(); 4 //入隊操作 5 for (int i = 0; i < 10; i++) 6 { 7 quereList.Enqueue(i + 100); 8 } 9 //出隊操作 10 while (quereList.Count != 0) 11 { 12 Console.WriteLine(quereList.Dequeue()); 13 } 14 }
6. Stack<T> 棧
后進先出,入棧(push)和出棧(pop)兩個操作
特別注意:Stack<T>不是線程安全
1 { 2 Console.WriteLine("---------------------------06 Stack<T> 棧-----------------------------------"); 3 Stack<int> stackList = new Stack<int>(); 4 //入棧操作 5 for (int i = 0; i < 10; i++) 6 { 7 stackList.Push(i + 100); 8 } 9 //出棧操作 10 while (stackList.Count != 0) 11 { 12 Console.WriteLine(stackList.Pop()); 13 } 14 }
7. Hashtable
典型的空間換時間,存儲數據不能太多,但增刪改查速度非常快。
特別注意:Hashtable是線程安全的,不需要配合鎖使用。
{ Console.WriteLine("---------------------------07 Hashtable-----------------------------------"); Hashtable tableList = new Hashtable(); //存儲 tableList.Add("001", "馬茹"); tableList["002"] = "二胖"; //查詢 foreach (DictionaryEntry item in tableList) { Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString()); } }
8. Dictionary<K,T>字典 (泛型的Hashtable)
增刪改查速度非常快,可以用來代替實體只有id和另一個屬性的時候,大幅度提升效率。
特別注意:Dictionary<K,T>不是線程安全,在多線程中需要配合鎖機制來進行,如果不想使用鎖,線程安全的字典為 ConcurrentDictionary。
1 { 2 Console.WriteLine("---------------------------08 Dictionary<K,T>字典-----------------------------------"); 3 Dictionary<string, string> tableList = new Dictionary<string, string>(); 4 //存儲 5 tableList.Add("001", "馬茹"); 6 tableList.Add("002", "二胖"); 7 tableList["002"] = "三胖"; 8 //查詢 9 foreach (var item in tableList) 10 { 11 Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString()); 12 } 13 }
強調:
以上8種類型,除了Hashtable是線程安全,其余都不是,都需要配合lock鎖來進行,或者采用 ConcurrentXXX來替代。
詳細的請見:http://www.cnblogs.com/yaopengfei/p/8322016.html
二. 四大接口比較
1. IEnumerable
是最基本的一個接口,用於迭代使用,里面有GetEnumerator方法。
2. ICollection
繼承了IEnumerable接口,主要用於集合,內部有Count屬性表示個數,像ArrayList、List、LinkedList均實現了該接口。
3. IList
繼承了IEnumerable 和 ICollection,實現IList接口的數據接口可以使用索引訪問,表示在內存上是連續分配的,比如Array、List。
4. IQueryable
這里主要和IEnumerable接口進行對比。
Enumerable里實現方法的參數是Func委托,Queryable里實現的方法的參數是Expression表達式。
實現IQueryable和IEnumabler均為延遲加載,但二者的實現方式不同,前者為迭代器模式,參數為Func委托,后者為Expression表達式目錄樹實現。
三. yield關鍵字
1. yield必須出現在IEunmerable中
2. yield是迭代器的狀態機,能做到延遲查詢,使用的時候才查詢,可以實現按序加載
3. 例子
測試一:在 “var data1 = y.yieldWay();”加一個斷點,發現直接跳過,不能進入yieldWay方法中,而在“foreach (var item in data1)”加一個斷點,第一次遍歷的時候就進入了yieldWay方法中,說明了yield是延遲加載的,只有使用的時候才查詢。
測試二:對yieldWay和commonWay獲取的數據進行遍歷,通過控制台發現前者是一個一個輸出,而后者是先一次性獲取完,一下全部輸出來,證明了yield可以做到按需加載,可以在foreach中加一個限制,比如該數據不滿足>100就不輸出。

1 //********************************* 下面為對比普通返回值和使用yeild返回值的方法 ************************************************ 2 3 /// <summary> 4 /// 含yield返回值的方法 5 /// </summary> 6 /// <returns></returns> 7 public IEnumerable<int> yieldWay() 8 { 9 for (int i = 0; i < 10; i++) 10 { 11 yield return this.Get(i); 12 } 13 } 14 /// <summary> 15 /// 普通方法 16 /// </summary> 17 /// <returns></returns> 18 public IEnumerable<int> commonWay() 19 { 20 int[] intArray = new int[10]; 21 for (int i = 0; i < 10; i++) 22 { 23 intArray[i] = this.Get(i); 24 } 25 return intArray; 26 } 27 28 /// <summary> 29 /// 一個獲取數據的方法 30 /// </summary> 31 /// <param name="num"></param> 32 /// <returns></returns> 33 private int Get(int num) 34 { 35 Thread.Sleep(1000); 36 return num * DateTime.Now.Second; 37 }
1 Console.WriteLine("-----------------------下面是調用yield方法-----------------------"); 2 yieldDemo y = new yieldDemo(); 3 var data1 = y.yieldWay(); 4 foreach (var item in data1) 5 { 6 Console.WriteLine(item); 7 } 8 Console.WriteLine("-----------------------下面是調用普通方法-----------------------"); 9 var data2 = y.commonWay(); 10 foreach (var item in data2) 11 { 12 Console.WriteLine(item); 13 }
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。