今天发现ms在nuget上发布了一个immutable collection的程序集,提供了对不可变对象的集合的支持。
简单的看了一下,貌似支持的还比较全:
immutablearray<t>
immutablestack<t>
immutablequeue<t>
immutablelist<t>
immutablehashset<t>
immutablesortedset<t>
immutabledictionary<k, v>
immutablesorteddictionary<k, v>
使用方式比较简单,一个简单的示例如下(对对immutable特性不熟悉的朋友请注意输出结果和list的区别):
var color1 = immutablearray.create("orange", "red", "blue"); var color2 = color1.add("black"); console.writeline(">>> color1: " + color1); console.writeline(">>> color2: " + color2);
immutable builders
由于immutable对象的更改操作是生成你一个新的对象,因此当频繁更改时,开销是比较大的。因此,和传统的immutable对象string有一个stringbuild一样,对于immutable集合,也提供了相应的immutable builder对象来进行批量更新操作。
为了方便使用,还提供了两个扩展函数tobuilder()和toimmutable()在immutable builder和immutable集合间快速互相转换。
var color2builder = color1.tobuilder(); color2builder.add("black"); color2builder.add("white"); var color2 = color2builder.toimmutable();
性能
下表是ms给出的基本集合操作的性能,还是令人满意的。具体的数据结构暂时没有时间去研究它,感觉大部分应该都是树。
| mutable (amortized) | mutable (worst case) | immutable |
---|---|---|---|
stack.push | o(1) | o(n) | o(1) |
queue.enqueue | o(1) | o(n) | o(1) |
list.add | o(1) | o(n) | o(log n) |
hashset.add | o(1) | o(n) | o(log n) |
sortedset.add | o(log n) | o(n) | o(log n) |
dictionary.add | o(1) | o(n) | o(log n) |
sorteddictionary.add | o(log n) | o(n log n) | o(log n) |
不过,由于每次对集合操作都会生成新的副本(并不会拷贝集合成员),应该是有额外的内存开销的,从它的性能上来看,应该是一种空间换时间的做法,有空再研究一下。
使用场景
immutable由于具有不可变性,天生是线程安全的,因此非常适宜于多线程场景。例如,在遍历的时候,为了防止遍历期间集合被破坏,传统的做法有如下两种
1. 锁定法:
lock (list) { foreach (var item in list) { //do something } }
如果遍历的时间较长,会长期锁定集合,导致其它的调用处饿死。为了解决这种情况,又有下一种做法。
2. 副本法
lock (list) { var listcopy = list.toarray(); } foreach (var item in listcopy) { //do something }
这种方式的最大问题是每次遍历都要生成副本,如果遍历比较频繁则开销较大。ps:这种场景下仍然需要lock(生成副本的时候)。
另外,这两种地方都需要对对象加锁,加锁除了影响性能外,还需要在每一个使用的地方都加锁,并且还需要避免死锁。这个基本上和内存泄漏一样对程序员来说是是一个非常大的负担
而immutable集合天生线程安全,可以不用加锁直接遍历,不仅性能更加优异,代码也更加优雅,能帮助我们快速实现稳定高效的程序。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持代码网。
发表评论