建议20、使用泛型集合来替代非泛型集合

http://www.cnblogs.com/aehyok/p/3384637.html 这里有一篇文章,是我之前专门来介绍泛型的。我们应尽量的使用泛型集合。因为泛型的确有它的好处:

1、提供了类型安全,在编译期间就可以检查错误

2、更重要的是大部分情况下泛型集合的性能比非泛型集合的性能都高很多。

下面我们来看一段简单的测试性能的代码:

class Program
{
static int collectionCount = 0;
static Stopwatch watch = null;
static int testCount = 10000000;
static void TestBegin()
{
GC.Collect(); ////强制对所有代码进行即时垃圾回收
GC.WaitForPendingFinalizers();////挂起线程,执行终结器队列中的终结器(即析构方法)
GC.Collect();///再次对所有代码进行垃圾回收,主要包括从终结器队列中出来的对象
collectionCount = GC.CollectionCount(0);///返回在0代中执行的垃圾回收次数
watch = new Stopwatch();
watch.Start();
} static void TestEnd()
{
watch.Stop();
Console.WriteLine("耗时:{0}",watch.ElapsedMilliseconds.ToString());
Console.WriteLine("垃圾回收次数:{0}", GC.CollectionCount(0) - collectionCount);
}
static void TestArrayList()
{
ArrayList arrayList = new ArrayList();
int temp = 0;
for (int i = 0; i < testCount; i++)
{
arrayList.Add(i);
temp = (int)arrayList[i];
}
arrayList = null;
} static void TestGenericList()
{
List<int> list = new List<int>();
int temp = 0;
for (int i = 0; i < testCount; i++)
{
list.Add(i);
temp = list[i];
}
list = null;
}
static void Main(string[] args)
{
Console.WriteLine("开始测试ArrayList");
TestBegin();
TestArrayList();
TestEnd();
Console.WriteLine("开始测试List<T>");
TestBegin();
TestGenericList();
TestEnd();
Console.ReadLine();
}
}

执行结果如下

  我上面测试的次数是10000000,可以发现,两者在垃圾回收次数和耗时都差距比较大,所以泛型集合有着非泛型集合无法超越的优势。所以还是尽量在我们的程序中使用泛型集合吧。

建议21、选择正确的集合

http://www.cnblogs.com/aehyok/p/3643928.html这里有一篇我刚写的关于集合的博文,主要是简单介绍了一下关于自己使用比较频繁的几个集合。

如果集合的数目固定并且不涉及转型,使用数组效率高,否则就是使用List<T>。

像使用数组、ArrayList、List<T>、Dictionary<key,value>这些集合的有点就是插入和删除数据效率比较高,缺点就是查找的效率相对来说低一些。

关于队列可以参考http://msdn.microsoft.com/zh-cn/library/System.Collections.Queue(v=vs.80).aspx

关于栈可以参考http://msdn.microsoft.com/zh-cn/library/System.Collections.Stack(v=vs.110).aspx

建议22、确保集合的线性安全

   建议18中提到,foreach循环不能代替for循环的一个原因是在迭代过程中对集合本身进行了增删操作。将此场景移植到多线程场景中,就是本建议要阐述的重点:确保集合的线程安全。集合线程安全是指在多个线程上添加活删除元素时,线程之间必须保持同步。

  下面我们来通过实例来更详细的查看一下,先简单定义一个实体类

    public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
       static List<Person> list = new List<Person>()
{
new Person(){ Name="aehyok",Age=25},
new Person(){Name="Kris",Age=23},
new Person(){Name="Leo",Age=26}
};
static AutoResetEvent autoSet = new AutoResetEvent(false);
static void Main(string[] args)
{
Thread t1 = new Thread(() =>
{
///阻止当前线程
autoSet.WaitOne();
foreach (var item in list)
{
Console.WriteLine("t1:"+item.Name);
Thread.Sleep(1000);
}
});
t1.Start(); Thread t2 = new Thread(() =>
{
///通知t1可以执行代码
autoSet.Set();
Thread.Sleep(1000);
list.RemoveAt(2);
});
t2.Start(); Console.ReadLine();
}

再来简单分析一下这段代码,其实就是闲定义了一个List集合,然后又定义了一个 AutoRestEvent的实例,用于控制线程的。

接下来在Main函数中定义了两个线程,在线程一中将线程一暂停,然后当调用线程二的时候再来通知线程一继续运行。最终运行结果

主要是因为线程一在暂停之后,开始运行线程二随即线程一得到通知可以继续运行,通过代码可以发现都有Thread.Sleep(1000);也就是为了保证两个线程都还在运行期间,线程二移除了集合中的一个元素,那么当线程一再次循环的时候,导致了错误的发生。

早在泛型集合出现之前,非泛型集合一般会提供一个SyncRoot属性,要保证非泛型集合的线程安全,可以通过锁定该属性来实现。如果上面的集合用ArrayList代替,保证线程安全则应该在迭代和删除的时候都加上锁lock,代码如下所示:

        static ArrayList list = new ArrayList()
{
new Person(){ Name="aehyok",Age=25},
new Person(){Name="Kris",Age=23},
new Person(){Name="Leo",Age=26}
};
static AutoResetEvent autoSet = new AutoResetEvent(false);
static void Main(string[] args)
{
Thread t1 = new Thread(() =>
{
///阻止当前线程
autoSet.WaitOne();
lock (list.SyncRoot)
{
foreach (Person item in list)
{
Console.WriteLine("t1:" + item.Name);
Thread.Sleep(1000);
}
} });
t1.Start(); Thread t2 = new Thread(() =>
{
///通知t1可以执行代码
autoSet.Set();
Thread.Sleep(1000);
lock (list.SyncRoot)
{
list.RemoveAt(2);
} });
t2.Start(); Console.ReadLine();
}

运行结果就是线程一执行通过

如果你试过,那么会发现泛型集合没有这样的属性来进行加锁,必须要自己创建一个锁定对象来完成同步的任务。

所以第一个例子我们可以这样进行修改

 static List<Person> list = new List<Person>()
{
new Person(){ Name="aehyok",Age=25},
new Person(){Name="Kris",Age=23},
new Person(){Name="Leo",Age=26}
};
static object SyncObject = new object();
static AutoResetEvent autoSet = new AutoResetEvent(false);
static void Main(string[] args)
{
Thread t1 = new Thread(() =>
{
///阻止当前线程
autoSet.WaitOne();
lock (SyncObject)
{
foreach (var item in list)
{
Console.WriteLine("t1:" + item.Name);
Thread.Sleep(1000);
}
}
});
t1.Start();
Thread t2 = new Thread(() =>
{
///通知t1可以执行代码
autoSet.Set();
Thread.Sleep(1000);
lock (SyncObject)
{
list.RemoveAt(2);
}
});
t2.Start(); Console.ReadLine();
}

C#程序编写高质量代码改善的157个建议【20-22】[泛型集合、选择集合、集合的安全]的更多相关文章

  1. C#程序编写高质量代码改善的157个建议【16-19】[动态数组、循环遍历、对象集合初始化]

    前言   软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类.不管是数组还是集合类,它们都有各自的优缺点.如何使用好集合是我们在开发过程中必须掌握的技巧.不要小看这些技巧,一旦在开 ...

  2. C#程序编写高质量代码改善的157个建议[正确操作字符串、使用默认转型方法、却别对待强制转换与as和is]

    前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理解的东西,有些地方可能理解的不太到位,还望指正. 建议1. ...

  3. C#程序编写高质量代码改善的157个建议【13-15】[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]

    前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议13.为类型输出格式化字符串 建议14.正确实现浅拷贝和深 ...

  4. C#程序编写高质量代码改善的157个建议【10-12】[创建对象时需要考虑是否实现比较器、区别对待==和Equals]

    前言 建议10.创建对象时需要考虑是否实现比较器 建议11.区别对待==和Equals 建议12.重写Equals时也要重写GetHashCode 建议10.创建对象时需要考虑是否实现比较器 有对象的 ...

  5. C#程序编写高质量代码改善的157个建议【4-9】[TryParse比Parse、使用int?来确保值类型也可以为null、readonly和const、0值设为枚举的默认值、避免给枚举类型的元素提供显式的值、习惯重载运算符]

    建议4.TryParse比Parse好 如果注意观察,除string之外的所有的基元类型.会发现它们都有两个将字符串转换为自身类型的方法:Parse和TryParse.以类型double为例. 两者最 ...

  6. 博友的 编写高质量代码 改善java程序的151个建议

    编写高质量代码 改善java程序的151个建议 http://www.cnblogs.com/selene/category/876189.html

  7. 编写高质量代码--改善python程序的建议(六)

    原文发表在我的博客主页,转载请注明出处! 建议二十八:区别对待可变对象和不可变对象 python中一切皆对象,每一个对象都有一个唯一的标识符(id()).类型(type())以及值,对象根据其值能否修 ...

  8. 编写高质量代码--改善python程序的建议(八)

    原文发表在我的博客主页,转载请注明出处! 建议四十一:一般情况下使用ElementTree解析XML python中解析XML文件最广为人知的两个模块是xml.dom.minidom和xml.sax, ...

  9. 编写高质量代码改善java程序的151个建议——导航开篇

    2014-05-16 09:08 by Jeff Li 前言 系列文章:[传送门] 下个星期度过这几天的奋战,会抓紧java的进阶学习.听过一句话,大哥说过,你一个月前的代码去看下,慘不忍睹是吧.确实 ...

随机推荐

  1. 数据库求闭包,求最小函数依赖集,求候选码,判断模式分解是否为无损连接,3NF,BCNF

    1.说白话一点:闭包就是由一个属性直接或间接推导出的所有属性的集合. 例(1):   设有关系模式R(U,F),其中U={A,B,C,D,E,I},F={A→D,AB→E,BI→E,CD→I,E→C} ...

  2. C语言I作业08

    C语言I作业08 这个作业属于哪个课程 C语言程序设计ll 这个作业的要求在哪里 https://edu.cnblogs.com/campus/zswxy/SE2019-2/homework/9981 ...

  3. DS1302时钟芯片驱动程序

    /***************************************************************************** FileName : DS1302.c F ...

  4. 成功build Maven但eclipse中依然显示该工程有错误

    在mac pro的控制台中,成功执行 mvn package,但该工程在eclipse仍然显示有错误. 解决办法:右键该工程 -> Maven -> Update project,勾选 F ...

  5. 蓝牙5.0芯片NRF52810和NRF52832可进行mesh组网

    提供智能化mesh照明解决方案,在现有传统灯具的基础上,插入NRF52832/52810的照明Mesh模块,可以迅速升级现有的传统灯具,配合手机APP和服务器系统,使每一盏灯成为物联网的一个智能节点, ...

  6. git 使用详解(7)-- 远程仓库的使用

    要参与任何一个 Git 项目的协作,必须要了解该如何管理远程仓库.远程仓库是指 托管在网络上 的项目仓库,可能会有好多个,其中有些你 只能读,另外有些可以写.同他人协作开发某个项目时,需要管理这些远程 ...

  7. Java修炼——四种方式解析XML_DOM

    四种方式解析XML:DOM      JDOM   DOM4J    SAX 先写一个XML栗子: <?xml version="1.0" encoding="UT ...

  8. EasyUI整合SpringBoot,Spring Data对数据的分页操作

    EasyUI的用法可以看中文官网,看插件是如何使用的 EasyUI中文官网 前端页面 <table id="dg" title="My Users" cl ...

  9. 2018 ACM-ICPC南京区域赛题解

    解题过程 开场开A,A题shl看错题意,被制止.然后开始手推A,此时byf看错E题题意,开始上机.推出A的规律后,shl看了E题,发现题意读错.写完A题,忘记判断N=0的情况,WA+1.过了A后,sh ...

  10. Kerberos+SSH安装配置使用教程

    一.背景说明 最早听说KDC和Kerberos应该是大三的<应用密码学>,当时感觉这套对称密钥分发机制比非对称密钥的PKI分发机制要好理解.但几年下来由于现实中使用SSL的场景比较比(主要 ...