前言

先普及一下线程安全和类型安全

线程安全:

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
线程安全问题都是由全局变量及静态标量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
 
类型安全:

类型安全很大程度上可以等价于内存安全,类型安全的代码不会试图访问自己没被授权的内存区域。“类型安全”常被用来形容编程语言,其根据在于该门编程语言是否提供保障类型安全的机制;有的时候也用“类型安全”形容某个程序,判别的标准在于该程序是否隐含类型错误。类型安全的编程语言与类型安全的程序之间,没有必然联系。好的程序员可以使用类型不那么安全的语言写出类型相当安全的程序,相反的,差一点儿的程序员可能使用类型相当安全的语言写出类型不太安全的程序。绝对类型安全的编程语言暂时还没有。

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

1. Array(数组):

分配在连续内存中,不能随意扩展,数组中数值类型必须是一致的。数组的声明有两种形式:直接定义长度,然后赋值;直接赋值。

缺点:插入数据慢。

优点:性能高,数据再多性能也没有影响

特别注意:Array不是线程安全,在多线程中需要配合锁机制来进行,如果不想使用锁,可以用ConcurrentStack这个线程安全的数组来替代Array。

{
Console.WriteLine("---------------------------01 Array(数组)-----------------------------------");
//模式一:声明数组并指定长度
int[] array = new int[];
//数组的赋值通过下标来赋值
for (int i = ; i < array.Length; i++)
{
array[i] = i + ;
}
//数组的修改通过下标来修改
array[] = ;
//输出
for (int j = ; j < array.Length; j++)
{
Console.WriteLine(array[j]);
} //模式二:直接赋值
string[] array2 = new string[] { "二胖", "二狗" };
}

2. ArrayList(可变长度的数组)

不必在声明的时候指定长度,即长度可变;可以存放不同的类型的元素。

致命缺点:无论什么类型存到ArrayList中都变为object类型,使用的时候又被还原成原先的类型,所以它是类型不安全的,当值类型存入的时候,会发生装箱操作,变为object引用类型,而使用的时候,又将object类型拆箱,变为原先的值类型,这尼玛,你能忍?

结论:不推荐使用,建议使用List代替!!

特别注意:ArrayList不是线程安全,在多线程中需要配合锁机制来进行。

{
Console.WriteLine("---------------------------02 ArrayList(可变长度的数组)-----------------------------------");
ArrayList arrayList = new ArrayList();
arrayList.Add("二胖");
arrayList.Add("马茹");
arrayList.Add();
for (int i = ; i < arrayList.Count; i++)
{
Console.WriteLine(arrayList[i] + "类型为:" + arrayList[i].GetType());
}
}

3. List<T> (泛型集合) 推荐使用

内部采用array实现,但没有拆箱和装箱的风险,是类型安全的

特别注意:List<T>不是线程安全,在多线程中需要配合锁机制来进行,如果不想使用锁,可以用ConcurrentBag这个线程安全的数组来替代List<T>

{
Console.WriteLine("---------------------------03 List<T> (泛型集合)-----------------------------------");
List<string> arrayList = new List<string>();
arrayList.Add("二胖");
arrayList.Add("马茹");
arrayList.Add("大胖");
//修改操作
arrayList[] = "葛帅";
//删除操作
//arrayList.RemoveAt(0);
for (int i = ; i < arrayList.Count; i++)
{
Console.WriteLine(arrayList[i]);
}
}

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。

实际应用场景:利用队列解决高并发问题

{
Console.WriteLine("---------------------------05 Queue<T> 队列-----------------------------------");
Queue<int> quereList = new Queue<int>();
//入队操作
for (int i = ; i < ; i++)
{
quereList.Enqueue(i + );
}
//出队操作
while (quereList.Count != )
{
Console.WriteLine(quereList.Dequeue());
}
}

6. Stack<T> 栈

后进先出,入栈(push)和出栈(pop)两个操作

特别注意:Stack<T>不是线程安全

{
Console.WriteLine("---------------------------06 Stack<T> 栈-----------------------------------");
Stack<int> stackList = new Stack<int>();
//入栈操作
for (int i = ; i < ; i++)
{
stackList.Push(i + );
}
//出栈操作
while (stackList.Count != )
{
Console.WriteLine(stackList.Pop());
}
}

7. Hashtable

典型的空间换时间,存储数据不能太多,但增删改查速度非常快。

特别注意:Hashtable是线程安全的,不需要配合锁使用。

{
Console.WriteLine("---------------------------07 Hashtable-----------------------------------");
Hashtable tableList = new Hashtable();
//存储
tableList.Add("", "马茹");
tableList[""] = "二胖";
//查询
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。

{
Console.WriteLine("---------------------------08 Dictionary<K,T>字典-----------------------------------");
Dictionary<string, string> tableList = new Dictionary<string, string>();
//存储
tableList.Add("", "马茹");
tableList.Add("", "二胖");
tableList[""] = "三胖";
//查询
foreach (var item in tableList)
{
Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString());
}
}

强调: 

以上8种类型,除了Hashtable是线程安全,其余都不是,都需要配合lock锁来进行,或者采用 ConcurrentXXX来替代。

二. 四大接口比较

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就不输出。

//*********************************  下面为对比普通返回值和使用yeild返回值的方法  ************************************************

       /// <summary>
/// 含yield返回值的方法
/// </summary>
/// <returns></returns>
public IEnumerable<int> yieldWay()
{
for (int i = ; i < ; i++)
{
yield return this.Get(i);
}
}
/// <summary>
/// 普通方法
/// </summary>
/// <returns></returns>
public IEnumerable<int> commonWay()
{
int[] intArray = new int[];
for (int i = ; i < ; i++)
{
intArray[i] = this.Get(i);
}
return intArray;
} /// <summary>
/// 一个获取数据的方法
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
private int Get(int num)
{
Thread.Sleep();
return num * DateTime.Now.Second;
}
Console.WriteLine("-----------------------下面是调用yield方法-----------------------");
yieldDemo y = new yieldDemo();
var data1 = y.yieldWay();
foreach (var item in data1)
{
Console.WriteLine(item);
}
Console.WriteLine("-----------------------下面是调用普通方法-----------------------");
var data2 = y.commonWay();
foreach (var item in data2)
{
Console.WriteLine(item);
}

注:本文参考http://www.cnblogs.com/yaopengfei/p/9007336.html,侵删。

【C#复习总结】探究各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字的更多相关文章

  1. ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借

    ASP.NET MVC深入浅出系列(持续更新)   一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...

  2. 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字

    一. 各类数据结构比较及其线程安全问题 1. Array(数组): 分配在连续内存中,不能随意扩展,数组中数值类型必须是一致的.数组的声明有两种形式:直接定义长度,然后赋值:直接赋值. 缺点:插入数据 ...

  3. .NET面试题系列(五)数据结构(Array、List、Queue、Stack)及线程安全问题

    常用数据结构的时间复杂度 如何选择数据结构 Array (T[]) 当元素的数量是固定的,并且需要使用下标时. Linked list (LinkedList<T>) 当元素需要能够在列表 ...

  4. Swift Array copy 的线程安全问题

    Swift Array copy 的线程安全问题 NSArray 继承自 NSObject,属于对象,有 copy 方法.Swift 的 Array 是 struct,没有 copy 方法.把一个 A ...

  5. c++ - Create empty json array with jsoncpp - Stack Overflow

    python中multiprocessing.pool函数介绍_正在拉磨_新浪博客     multiprocessing.pool c++ - Create empty json array wit ...

  6. [Python数据结构] 使用List实现Stack

    [Python数据结构] 使用List实现Stack 1. Stack 堆栈(Stack)又称为栈或堆叠,是计算机科学中一种特殊的串列形式的抽象数据类型(ADT),其特殊之处在于只能允许在阵列的一端进 ...

  7. MySQL源码 数据结构array

    MySQL源码中自己定义了许多数据结构,放在mysys的目录下,源码中通常都使用这些数据结构来组织存放数据,也更容易实现跨平台.   下面先来看下MySQL定义的动态数组: [源代码include/a ...

  8. JAVA数据结构--Array数组实现

    所谓数组,是有序的元素序列. [1]  若将有限个类型相同的变量的集合命名,那么这个名称为数组名.组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量.用于区分数组的各个元素的数字编 ...

  9. java数据结构--array与ArrayList的区别

    ArrayList 内部是由一个array 实现的. 如果你知道array 和 ArrayList 的相似点和不同点,就可以选择什么时候用array 或者使用ArrayList , array 提供 ...

随机推荐

  1. 在windows下如何快速搭建web.py开发框架

    在windows下如何快速搭建web.py开发框架 用Python进行web开发的话有很多框架供选择,比如最出名的Django,tornado等,除了这些框架之外,有一个轻量级的框架使用起来也是非常方 ...

  2. ES搜索引擎集群模式搭建【Kibana可视化】

    一.简介 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎(与Solr类似),基于RESTful web接口.Elasticsearch是用Ja ...

  3. 新闻思考-阿里进军游戏产业,苹果发力ARM芯片

    2018.04.03 大家好,这是我开通博客的第一篇文章,我希望在这里分享我的知识,也学习更多的知识,希望大家学习愉快. 阿里进军游戏产业,拿下旅行青蛙的代理权.腾讯一直在进攻阿里的核心业务:电商和支 ...

  4. [20181007]12cR2 Using SQL Patch 2.txt

    [20181007]12cR2 Using SQL Patch 2.txt --//12cR2 已经把sql打补丁集成进入dbms_sqldiag,不是11g的 DBMS_SQLDIAG_INTERN ...

  5. mssql sqlserver for xml EXPLICIT 用法详解说明

    摘要:下文通过举例的方式,详细说明"for xml EXPLICIT"关键字的用法,如下所示:实验环境:sql server 2008 R2 EXPLICIT的功能:将数据表采用特 ...

  6. Hadoop2.7.6_01_部署

    1. 主机规划 主机名称 外网IP 内网IP 操作系统 备注 安装软件 mini01 10.0.0.11 172.16.1.11 CentOS 7.4 ssh port:22 Hadoop [Name ...

  7. 聚类——GAKFCM

    聚类——GAKFCM 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 参考文献:黄白梅. 基于GA优化的核模糊C均值聚类算法的研究[D]. 武汉科技大学 ...

  8. Python Numpy-基础教程

    目录 1. 为什么要学习numpy? 2. Numpy基本用法 2.1. 创建np.ndarry 2.2. Indexing and Slicing Boolean Index 2.3. Univer ...

  9. tomcat session 共享

    1. nginx+tomcat7+memcached 安装JDK7sudo apt-get install java7-jdk 安装tomcat7Tomcat7下载地址http://mirror.bj ...

  10. C#基础知识之泛型集合转换为DataTable

    在做项目中,遇到了将集合转换为DataTable的使用,在网上看了资料,在这里记录下来,分享. using System; using System.Collections.Generic; usin ...