zoukankan      html  css  js  c++  java
  • 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字

    一. 各类数据结构比较及其线程安全问题

    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         }
    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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    204. Count Primes (Integer)
    203. Remove Linked List Elements (List)
    202. Happy Number (INT)
    201. Bitwise AND of Numbers Range (Bit)
    200. Number of Islands (Graph)
    199. Binary Tree Right Side View (Tree, Stack)
    198. House Robber(Array; DP)
    191. Number of 1 Bits (Int; Bit)
    190. Reverse Bits (Int; Bit)
    189. Rotate Array(Array)
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/9007336.html
Copyright © 2011-2022 走看看