自定义不可变集合
  我们去枚举可变集合时,出于线程安全的考虑我们往往需要进行加锁处理,防止该集合在其他线程被修改,而使用不可变集合则能避免这个问题。我们平常使用的数据结构都是采用可变模式来实现的,那怎么实现一个不可变数据结构呢!以栈来示例,具体代码如下:
public interface IStack<T> : IEnumerable<T>
{
IStack<T> Push(T value);
IStack<T> Pop();
T Peek();
bool IsEmpty { get; }
}
public sealed class Stack<T> : IStack<T>
{
private sealed class EmptyStack : IStack<T>
{
public bool IsEmpty { get { return true; } }
public T Peek() { throw new Exception("Empty stack"); }
public IStack<T> Push(T value) { return new Stack<T>(value, this); }
public IStack<T> Pop() { throw new Exception("Empty stack"); }
public IEnumerator<T> GetEnumerator() { yield break; }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
private static readonly EmptyStack empty = new EmptyStack();
public static IStack<T> Empty { get { return empty; } }
private readonly T head;
private readonly IStack<T> tail;
private Stack(T head, IStack<T> tail)
{
this.head = head;
this.tail = tail;
}
public bool IsEmpty { get { return false; } }
public T Peek() { return head; }
public IStack<T> Pop() { return tail; }
public IStack<T> Push(T value) { return new Stack<T>(value, this); }
public IEnumerator<T> GetEnumerator()
{
for (IStack<T> stack = this; !stack.IsEmpty; stack = stack.Pop())
yield return stack.Peek();
}
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
  入栈时会实例化一个新栈对象
  将新值通过构造函数传入,并存放在新对象Head位置,旧栈对象放在在Tail位置引用
  出栈时返回当前栈对象的Tail引用的栈对象
  使用方法如下:
  IStack<int> s1 = Stack<int>.Empty;
  IStack<int> s2 = s1.Push(10);
  IStack<int> s3 = s2.Push(20);
  IStack<int> s4 = s3.Push(30);
  IStack<int> v3 = s4.Pop();
  foreach (var item in s4)
  {
  //dosomething
  }
  每次Push都是一个新对象,旧对象不可修改,这样在枚举集合不需要担心其他线程修改了。
  Net提供的不可变集合
  不可变队列,不可变列表等数据结构如果都自己实现工作量确实有点大。幸好的是Net在4.5版本已经提供了不可变集合的基础类库。 使用Nuget安装:
  Install-Package Microsoft.Bcl.Immutable
  使用如下,和上面我们自定义的几乎一样:
  ImmutableStack<int> a1 = ImmutableStack<int>.Empty;
  ImmutableStack<int> a2 = a1.Push(10);
  ImmutableStack<int> a3 = a2.Push(20);
  ImmutableStack<int> a4 = a3.Push(30);
  ImmutableStack<int> iv3 = a4.Pop();
  使用Net不可变列表集合有一点要注意的是,当我们Push值时要重新赋值给原变量才正确,因为push后会生成一个新对象,原a1只是旧值:
  ImmutableStack<int> a1 = ImmutableStack<int>.Empty;
  a1.Push(10);
  //不正确,a1仍是空值值,push会生成新的栈。
  a1 = a1.Push(10);
  //需要将新栈重新赋值给a1
  NET提供的常用数据结构
  ImmutableStack
  ImmutableQueue
  ImmutableList
  ImmutableHashSet
  ImmutableSortedSet
  ImmutableDictionary<K, V>
  ImmutableSortedDictionary<K, V>
  不可变集合和可变集合在算法复杂度上的不同: