第三節:深度剖析各種數據結構(Array、List、Queue、Stack)及線程安全問題和yeild關鍵字

一. 各種數據結構比較及其線程安全問題html

1. Array(數組):node

  分配在連續內存中,不能隨意擴展,數組中數值類型必須是一致的。數組的聲明有兩種形式:直接定義長度,而後賦值;直接賦值。數組

  缺點:插入數據慢。安全

  優勢:性能高,數據再多性能也沒有影響數據結構

  特別注意: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(可變長度的數組)併發

  沒必要在聲明的時候指定長度,即長度可變;能夠存放不一樣的類型的元素。ide

  致命缺點:不管什麼類型存到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         }
View Code
 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 : 原創博客請在轉載時保留原文連接或在文章開頭加上本人博客地址,不然保留追究法律責任的權利。
相關文章
相關標籤/搜索