前言

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

线程安全:

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

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

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

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

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. (后端)Mybatis实现批量删除操作(转)

    原文地址:https://blog.csdn.net/javaee_sunny/article/details/52511842 一. 这里主要考虑两种参数类型:数组或者集合. 而这点区别主要体现在E ...

  2. weui textarea超出字符被截断

    HTML: <div class="weui-cells weui-cells_form" style="margin-top: 0;"> < ...

  3. javascript语言之 this概念

    转载 猫猫小屋 http://www.maomao365.com/?p=837 由于javascript是一种解释性语言,运行时才会解释所有的变量值,所以对于javascript中this所指的对象是 ...

  4. SQL Server 锁实验(UPDATE加锁探究)

    update语句: 本例中由于看到的是update执行完的锁情况,因此无法看到IU锁,但其实针对要修改的数据页和索引页会先加IU锁,记录和键先加U锁,然后再转化为IX和X锁. 如果想要看到IU锁和U锁 ...

  5. 挂载KVM Guest操作系统磁盘

    使用虚拟机时, 发现想要修改虚拟机中的文件非常麻烦, 需要启动虚拟机, 然后再登录进去修改. 对于已经关闭的虚拟机, 为了修改一个文件而启动, 非常耽误时间. 对于一个无法启动的虚拟机(比如启动文件损 ...

  6. March 11th, 2018 Week 11th Sunday

    All good things must come to an end. 好景无常. Love is when the other person's happiness is more importa ...

  7. iOS弹出UIViewController小视图

    在TestViewController1中弹出TestViewController2 在TestViewController中点击按钮或者什么触发方法里面写入以下代码 TestViewControll ...

  8. Python3基础-函数实例学习

    内置函数 绝对值函数 x = abs(100) y = abs(-20) print('x=100的绝对值为:{}'.format(x)) print('y=-20的绝对值为:{}'.format(y ...

  9. 阿里云短信服务调用例子-Python

    阿里云短信服务调用例子 阿里云官方文档https://helpcdn.aliyun.com/document_detail/101893.html 首先需要安装阿里云PythonSDK(下面是pyth ...

  10. UDP Health Checks

    This chapter describes how to configure different types of health checks for UDP servers in a load-b ...