您的位置:首页 > 其它

Immutable Collections(2)ImmutableList<T>实现原理.(上)

2013-05-06 21:52 302 查看
[b]ImmutableCollections(2)ImmutableList<T>实现原理.(上)[/b]
文/玄魂

前言

在上一篇文章(ImmutableCollections(1)),我简要说明了不可变集合的基本概念和简单应用。从本篇博文开始,会探讨下几个典型集合类型的内部实现机制。本篇博客主要探讨ImmutableList<T>实现原理。

博文中引用的代码并非是.NET源码,而是反编译得来,不正确之处,还望指教。

2.1概述

下图是ImmutableList<T>类型包含的核心字段、属性(并非全部),以及和其他类型的关系。这张图是自动生成的,我直接拿过来没有做什么改动,可能会让人云里雾里,下面我做简要的说明。



处于最顶端的ImmutableList静态类,是ImmutableList<T>类型的构造者,它或者直接返回ImmutableList<T>的Empty属性,或者在Empty的基础上构造ImmutableList<T>实例,比如下面的代码:

publicstaticImmutableList<T>Create<T>()

{

returnImmutableList<T>.Empty;

}

publicstaticImmutableList<T>Create<T>(IEqualityComparer<T>equalityComparer)

{

returnImmutableList<T>.Empty.WithComparer(equalityComparer);

}

publicstaticImmutableList<T>Create<T>(Titem)

{

returnImmutableList<T>.Empty.Add(item);

}

ImmutableList静态类下面是核心部分——ImmutableList<T>类型。ImmutableList<T>继承自如下接口:


IImmutableList<T>,IReadOnlyList<T>,IReadOnlyCollection<T>,IList<T>,ICollection<T>,IList,ICollection,IOrderedCollection<T>,IEnumerable<T>,IEnumerable,IImmutableListQueries<T>

其中IImmutableList<T>定义如下:

publicinterfaceIImmutableList<T>:IReadOnlyList<T>,IReadOnlyCollection<T>,IEnumerable<T>,IEnumerable

{

IEqualityComparer<T>ValueComparer

{

get;

}

IImmutableList<T>Clear();

boolContains(Tvalue);

intIndexOf(Tvalue);

IImmutableList<T>Add(Tvalue);

IImmutableList<T>AddRange(IEnumerable<T>items);

IImmutableList<T>Insert(intindex,Telement);

IImmutableList<T>InsertRange(intindex,IEnumerable<T>items);

IImmutableList<T>Remove(Tvalue);

IImmutableList<T>RemoveAll(Predicate<T>match);

IImmutableList<T>RemoveRange(IEnumerable<T>items);

IImmutableList<T>RemoveRange(intindex,intcount);

IImmutableList<T>RemoveAt(intindex);

IImmutableList<T>SetItem(intindex,Tvalue);

IImmutableList<T>Replace(ToldValue,TnewValue);

IImmutableList<T>WithComparer(IEqualityComparer<T>equalityComparer);

}

IImmutableListQueries<T>定义如下:

internalinterfaceIImmutableListQueries<T>

{

intCount

{

get;

}

ImmutableList<TOutput>ConvertAll<TOutput>(Func<T,TOutput>converter);

voidForEach(Action<T>action);

ImmutableList<T>GetRange(intindex,intcount);

voidCopyTo(T[]array);

voidCopyTo(T[]array,intarrayIndex);

voidCopyTo(intindex,T[]array,intarrayIndex,intcount);

boolExists(Predicate<T>match);

TFind(Predicate<T>match);

ImmutableList<T>FindAll(Predicate<T>match);

intFindIndex(Predicate<T>match);

intFindIndex(intstartIndex,Predicate<T>match);

intFindIndex(intstartIndex,intcount,Predicate<T>match);

TFindLast(Predicate<T>match);

intFindLastIndex(Predicate<T>match);

intFindLastIndex(intstartIndex,Predicate<T>match);

intFindLastIndex(intstartIndex,intcount,Predicate<T>match);

intIndexOf(Titem);

intIndexOf(Titem,intindex);

intIndexOf(Titem,intindex,intcount);

intLastIndexOf(Titem);

intLastIndexOf(Titem,intindex);

intLastIndexOf(Titem,intindex,intcount);

boolTrueForAll(Predicate<T>match);

}

其他接口,是.NET中原有接口,这里就不列举了。IImmutableList<T>的核心行为都定义在这两个接口当中。

SyncRoot是Object类型字段,作为同步锁对象。

Empty直接返回当前集合的单例对象EmptySingleton。



ImmutableList<T>构造函数如下:

internalImmutableList()

{

this.root=ImmutableList<T>.Node.EmptyNode;

this.valueComparer=EqualityComparer<T>.Default;

}

privateImmutableList(ImmutableList<T>.Noderoot,IEqualityComparer<T>valueComparer)

{

root.Freeze();

this.root=root;

this.valueComparer=valueComparer;

}

在构造函数中我们又发现两个很重要的类型,Node和IEqualityComparer。IEqualityComparer这里就不解释了,我们重点关注Node,从字面上理解,这是一个表示节点的类,事实上它是ImmutableList<T>的核心,数据的承载和操作都是对Node类的包装。下面我们来看看Node的庐山真面目。

2.2Node

Node类继承自三个接口,


internalsealedclassNode:IBinaryTree<T>,IEnumerable<T>,IEnumerable

我们主要关注IBinaryTree<T>,定义如下:

interfaceIBinaryTree<outT>

{

intHeight

{

get;

}

TValue

{

get;

}

IBinaryTree<T>Left

{

get;

}

IBinaryTree<T>Right

{

get;

}

boolIsEmpty

{

get;

}

intCount

{

get;

}

}

接口很清楚,定义了一个二叉树,但是这棵二叉树的具体特性但从接口上还无从得知。现在我们再看Node类。



ImmutableList<T>的EmptySingleton就是返回的上图中最上面的Node类的EmptyNode。EmptyNode定义如下:

internalstaticreadonlyNodeEmptyNode=newNode();

Node类的Key和Value属性是同一个值,就是当前节点的值。在代码中都是以Key为操作对象。下面分析Node的相关行为的时候,会有更清楚的认识。

frozen是一个bool类型的变量,表示是否冻结。冻结可以说是不可变集合的一个关键特性,下面也会对此做详细的分析。

height是以当前节点为根的树的高度。

this.height=1+Math.Max(left.height,right.height);

count以当前节点为根的树的节点个数。

this.count=1+left.count+right.count;

IsEmpty判断是否有子节点。

publicboolIsEmpty

{

get

{

returnthis.left==null;

}

}

right和left就是左右子树。

2.3行为

2.3.1初始化

我们通过下面的代码来观察Node的初始化过程。

staticvoidMain(string[]args)

{

varfruitBasket=ImmutableList.Create<string>();

varass=fruitBasket.Add("ddd");

}

启动程序,首先进入ImmutableList.Create<string>()方法。



Create方法直接返回ImmutableList<T>.Empty,Empty属性直接返回EmptySingleton。


调用EmptySingleton时触发ImmutableList<T>的初始化。



接下来在构造函数中调用Node.EmptyNode。





在Node类的无参构造函数中只初始化了一个变量:



此时Node.EmptyNode实例的各字段值如下图所示:



到此为止第一次初始化结束。

现在测试下带比较器的构造方式,先新建一个TestCompare类:

classTestCompare<T>:IEqualityComparer<T>

{

publicboolEquals(Tx,Ty)

{

returnx.Equals(y);

}

publicintGetHashCode(Tobj)

{

returnthis.GetHashCode();

}

}

然后更改Main函数中的代码:

staticvoidMain(string[]args)

{

varfruitBasket=ImmutableList.Create<string>(newTestCompare<string>());

varass=fruitBasket.Add("ddd");

}

ImmutableList静态方法仍然在ImmutableList<T>.Empty基础上调用了WithCompare方法。




WithCompare方法在初始化了valueCompare字段之后调用了构造函数ImmutableList(ImmutableList<T>.Noderoot,IEqualityComparer<T>valueComparer):


privateImmutableList(ImmutableList<T>.Noderoot,IEqualityComparer<T>valueComparer)

{

Requires.NotNull<ImmutableList<T>.Node>(root,"root");

Requires.NotNull<IEqualityComparer<T>>(valueComparer,"valueComparer");

root.Freeze();

this.root=root;

this.valueComparer=valueComparer;

}

上面的构造函数,调用了Freeze()方法,

internalvoidFreeze()

{

if(!this.frozen)

{

this.left.Freeze();

this.right.Freeze();

this.frozen=true;

}

}

这段代码,实际上是一个递归调用,设置每个节点为冻结状态。

带初始值的构造函数,实际是调用了Add方法,我将在下一篇博文中单独分析。

本篇博文到此结束,未完,待续。。。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: