C# 泛型集合
考虑到泛型在集合类型中的广泛应用,这里一起讨论。
1. 泛型
1.1 泛型的定义与约束
创建泛型方法、委托、接口或类时,需要在名称后增加尖括号及其中的泛型参数,泛型参数通常用T或T为前缀的描述性单词表示。
/// <summary>
/// 集合类型扩展方法
/// </summary>
public static class CollectionExt
{
/// <summary>
/// 集合类型判空
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection"></param>
/// <returns></returns>
public static bool IsNullOrEmpty<T>(this ICollection<T> collection)
{
return collection == null || collection.Count == ;
}
}
可用where来约束泛型继承的基类或实现的接口,并且可以使用default(T)获取泛型的默认值。
/// <summary>
/// 获取集合中按照某个数值类型比较 最小的元素
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="func"></param>
/// <returns></returns>
public static T MinItem<T>(this IEnumerable<T> items, Func<T, decimal> func) where T : class
{
List<decimal> list = new List<decimal>();
if (items.IsNullOrEmpty())
{
return null;
} decimal minValue = decimal.MaxValue;
T minItem = default(T);
foreach (var item in items)
{
var currentValue = func(item);
if (minValue > currentValue)
{
minItem = item;
minValue = currentValue;
}
}
return minItem;
}
1.2 泛型中的类型膨胀
面试的时候,常常会被问到C#和JAVA中泛型的差异,以及泛型参数类型在何时确定。在C#中,仍然以第一段代码中集合类型判空为例分析。
class Program
{
static void Main(string[] args)
{
var values = new List<int>();
var strs = new List<string>();
Console.WriteLine(values.IsNullOrEmpty());
Console.WriteLine(strs.IsNullOrEmpty());
Console.ReadKey();
}
}
查看IL代码。
.entrypoint
// 代码大小 44 (0x2c)
.maxstack
.locals init ([] class [mscorlib]System.Collections.Generic.List`<int32> values,
[] class [mscorlib]System.Collections.Generic.List`<string> strs)
IL_0000: nop
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`<int32>::.ctor()
IL_0006: stloc.
IL_0007: newobj instance void class [mscorlib]System.Collections.Generic.List`<string>::.ctor()
IL_000c: stloc.
IL_000d: ldloc.
IL_000e: call bool ConsoleApplication1.CollectionExt::IsNullOrEmpty<int32>(class [mscorlib]System.Collections.Generic.ICollection`<!!>)
IL_0013: call void [mscorlib]System.Console::WriteLine(bool)
IL_0018: nop
IL_0019: ldloc.
IL_001a: call bool ConsoleApplication1.CollectionExt::IsNullOrEmpty<string>(class [mscorlib]System.Collections.Generic.ICollection`<!!>)
IL_001f: call void [mscorlib]System.Console::WriteLine(bool)
IL_0024: nop
IL_0025: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_002a: pop
IL_002b: ret
可以看到,泛型的参数类型在编译时就已经确定。
编译后生成的List`1<int32>和List`1<string>,是两个不同的类型,这称为类型膨胀,基于这种方法实现的泛型被称为真实泛型。
这就使得类型安全,避免了装箱和拆箱操作。
2. 集合类型
System.Array定义了数组类。 System.Collections命名空间中定义了处理object对象的常用集合类,在访问时需类型转换或装箱拆箱。 System.Collections.Generic命名空间中提供了很多集合类的泛型版本。 System.Collections.Specialized命名空间中定义了专用于特定类型的集合类。 .NET 4.0开始提供System.Collections.Concurrent命名空间中的多个线程安全的集合类。
不急,慢慢来。
之前遇到几个工作七八年的竟然也不懂这些很基础的东西。
可以说劣币驱逐良币,也是离开目前公司的主要原因,不过社会本身如此。不管怎样,还是多看看、多学习,进而跳出牢笼,重新认识自我和社会,才有可能抓住转瞬即逝的机会。
从自身的角度,觉得技术和成长好厉害、解决很多问题,其实是没有用滴,老板考虑的始终是用较为低廉的薪水招到能做一般事情的人就行了,毕竟大家基本都是业务驱动的公司,能卖钱才是正道理,所以能说的永远比能做事情的机会多。
2.1 IEnumerable
简单的说,实现此接口,才能支持遍历,接口返回的是一个IEnumerator。
[ComVisible(true)]
[Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]
public interface IEnumerable
{
[DispId(-)]
IEnumerator GetEnumerator();
}
2.2 IEnumerator
真正的实现了遍历的过程,还延伸到yield的用法,这里不再赘述。
[ComVisible(true)]
[Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")]
public interface IEnumerator
{
object Current { get; } bool MoveNext(); void Reset();
}
Current当前枚举项的引用,初始化时尚未指向任何枚举项,每次访问时要转化为对应类型,故多次使用当前枚举项时可先赋值给强类型的临时变量,但泛型版本则不需要这样做。
MoveNext()移动引用使其指向下一个枚举项,首次移动后指向第一个枚举项,重复整个过程直至当前枚举项为空实现对集合的遍历。
Reset()将当前枚举项的引用重置到初始状态。
从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程,即使一个集合本身是线程安全的,其他线程仍可以修改该集合,这将导致枚举数引发异常。如果说写七八年代码的连这都不懂,我信(被前前同事坑怕了,中毒太深)。
若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合。
2.3 ICollection
[ComVisible(true)]
public interface ICollection : IEnumerable
{
int Count { get; } bool IsSynchronized { get; } object SyncRoot { get; } void CopyTo(Array array, int index);
}
各种实现类中,有几个比较关键的属性:
Capacity属性表示的是集合的容量。 Count属性表示的是集合中当前数据项个数,按照各自的规则自动扩容。 Clear操作只是清空集合中的元素,但集合的容量没变,仍然占用着内存空间,类似于乘客从列车中出来,但列车的容量不变。 SyncRoot属性主要在需要线程安全的操作时使用。
2.4 IDictionary
属于一对一的字典,Keys和Values独立存储,通过对Key执行哈希获取Value的存储位置,所以Key可以是非null的任意类型,且不能有重复。
[ComVisible(true)]
[DefaultMember("Item")]
public interface IDictionary : ICollection, IEnumerable
{
object this[object key] { get; set; } bool IsReadOnly { get; } ICollection Keys { get; } ICollection Values { get; } void Add(object key, object value); void Clear(); bool Contains(object key); IDictionaryEnumerator GetEnumerator(); void Remove(object key);
}
在具体的实现类中:
Dictionary<TKey,TValue>采用链地址法处理哈希冲突,填充因子是1。
ConcurrentDictionary<TKey,TValue>线程安全的键值集合,TyrAdd、TryGetValue、TryRemove、TryUpdate方法以非阻塞的方式访问元素。
SortedList<TKey,TValue>用两个数组分别存储键和值并保持大小的同步性,所以对GC比较友好一点,且支持用索引或键查找值并按照键排序。
SortedDictionary<TKey,TValue>是一个按照键构造的二叉查找树,所以,添加或删除操作只会根据红黑树的特性旋转节点来保持平衡故性能优于SortedList而差于Dictionary,而查找时可利用二分法,结构上对GC而言相对比较复杂。
其中,Dictionary[key]=value相当于AddOrUpdate,属于引用替换,亲们可以考虑下是不是线程安全的。
2.5 ILookup
属于一对多的字典类型。
[DefaultMember("Item")]
public interface ILookup<TKey, TElement> : IEnumerable<IGrouping<TKey, TElement>>, IEnumerable
{
IEnumerable<TElement> this[TKey key] { get; } int Count { get; } bool Contains(TKey key);
}
2.6 IList
提供对数据项的增删操作,可按照索引访问数据项。
[ComVisible(true)]
[DefaultMember("Item")]
public interface IList : ICollection, IEnumerable
{
object this[int index] { get; set; } bool IsFixedSize { get; } bool IsReadOnly { get; } int Add(object value); void Clear(); bool Contains(object value); int IndexOf(object value); void Insert(int index, object value); void Remove(object value); void RemoveAt(int index);
}
索引符是一种特殊类型的属性,提供按照索引访问数据项的功能。
遍历删除的操作,可以考虑使用倒叙方式。
2.7 ISet
不允许有重复的元素。
public interface ISet<T> : ICollection<T>, IEnumerable<T>, IEnumerable
{
bool Add(T item); void ExceptWith(IEnumerable<T> other); void IntersectWith(IEnumerable<T> other); bool IsProperSubsetOf(IEnumerable<T> other); bool IsProperSupersetOf(IEnumerable<T> other); bool IsSubsetOf(IEnumerable<T> other); bool IsSupersetOf(IEnumerable<T> other); bool Overlaps(IEnumerable<T> other); bool SetEquals(IEnumerable<T> other); void SymmetricExceptWith(IEnumerable<T> other); void UnionWith(IEnumerable<T> other);
}
这里牢骚几句,好多程序员处理去重时,都用dictionary(键值一样),明明有set不用,真捉急。
还有一些场景,比如电商产品起价的更新时,需要对数据取交集、并集、差集的运算,此时set也很有妙用,可以将复杂度从N^2降低到N。
C# 泛型集合的更多相关文章
- C# DataSet装换为泛型集合
1.DataSet装换为泛型集合(注意T实体的属性其字段类型与dataset字段类型一一对应) #region DataSet装换为泛型集合 /// <summary> /// 利用反射和 ...
- Linq操作非泛型集合
我们都知道,Linq能查询泛型集合,确切的说是:LINQ能实现查询泛型对象或者实现了IEnumerable.但是,很遗憾的是诸如ArrayList这样的非泛型集合并没有实现IEnumerable.那咋 ...
- c#利用泛型集合,为自己偷偷懒。
有人说"越懒"的程序员进步的越快!其实还挺有道理.亲身体验,从刚出来工作到现在,自己变"懒"了许多,但感觉写出来的代码确有了不少提升.刚开始啊,同样的代码,赋值 ...
- 2016年11月27日--面向对象:多态、类库、委托、is和as运算符、泛型集合
1.虚方法 virtual 重写 override 父类中的方法,在子类中并不适用,那么子类需要自主更改继承的方法或者是属性,那父类中加了virtual关键字的方法才可以被子类重写,子类重写父类的方法 ...
- GsonUtils.getGson().fromJson() 转泛型集合
List<QiTaFree> qiTaFreeList = GsonUtils.getGson().fromJson(exhiMain.getQiTaFressJson(), new Ty ...
- 快速入门系列--CLR--03泛型集合
.NET中的泛型集合 在这里主要介绍常见的泛型集合,很多时候其并发时的线程安全性常常令我们担忧.因而简述下.NET并发时线程安全特性,其详情请见MSDN. 普通集合都不支持多重并发写操作 部分支持单线 ...
- 类库,委托,as.is,var,泛型集合
类库: 就是让别人调用你写的方法,并且不让别人看到你是怎么实现的.(比如说一些核心文件) 如果有功能你不会做,需要别人帮忙,那么你的同事可以帮你写好一个类,然后你来调用这个类中的方法,完成你的项目. ...
- 面向对象——is和as运算符、泛型集合 List<T>
二:is和as运算符: (1) is运算符 is 运算符用于检查对象是否与给定类型兼容.如果兼容返回true,否则返回false; 一般用于查看某个类是否实现了某个接口,或者是不是某个类的子类; 例如 ...
- Reflection和Expression Tree解析泛型集合快速定制特殊格式的Json
很多项目都会用到Json,而且大部分的Json都是格式固定,功能强大,转换简单等,标准的key,value集合字符串:直接JsonConvert.SerializeObject(List<T&g ...
- C#—类库、委托、is和as运算符、泛型集合
类库 类库(Class Library)是一个综合性的面向对象的可重用类型集合,这些类型包括:接口.抽象类和具体类.类库可以解决一系列常见编程任务(包括诸如字符串管理.数据收集.数据库连接以及文件访问 ...
随机推荐
- 'IFileDialog' : no GUID has been associated with this object
在VS2010中使用IFileDialog,提示一下错误: 'IFileDialog' : no GUID has been associated with this object MSDN上明确表示 ...
- css基础内容之background
html如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...
- Oracle - 简单理解字符集
起因 今天使用SoapUI来模拟第三方的程序进行测试. 简单来说,流程是我们的程序从SoapUI里面获取数据,然后将数据插入数据库从数据库. SoapUI的脚本来自于第三方,里面包含当地的字符编码,当 ...
- 【转载】B树、B-树、B+树、B*树
转载自http://blog.csdn.net/manesking/archive/2007/02/09/1505979.aspx B树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和 ...
- 去掉Windows桌面软件的快捷图标的箭头
去掉Windows桌面软件的快捷图标的箭头 怎么去除桌面快捷方式图标箭头 cmd /k reg delete "HKEY_CLASSES_ROOT\lnkfile" /v IsSh ...
- SourceTree 简单使用 for Mac
系统版本: 10.12.6 SourceTree版本:2.3.1 (中文版)SourceThree安装包 密码:9jc3 传送门 目录 1.创建gitHub账号和仓库 2.sourceTree管理gi ...
- Web初学-Web应用细节
一.web应用程序简介 WEB应用程序指供浏览器访问的程序,通常也简称为web应用. 一个web应用由多个静态web资源和动态web资源组成,如: html.css.js文件 Jsp文件.java程序 ...
- Eclipse常用快捷键和调试方法
原文链接:http://my.oschina.net/u/1054538/blog/741561 常用快捷键 Eclipse最全快捷键,熟悉快捷键可以帮助开发事半功倍,节省更多的时间来用于做有意义的事 ...
- Eclipse知识
http://www.runoob.com/eclipse/eclipse-create-jar-files.html Eclipse 生成jar包 打开 Jar 文件向导 Jar 文件向导可用于将项 ...
- webSocket浏览器握手不成功(解决)
websocket与服务端握手会报握手不成功的错误解决方法: 首先是服务端首次收到请求要回报给客户端的报文要做处理多的不说,方法敬上: /// <summary> /// 打包请求连接数据 ...