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

简介:
Immutable Collections2ImmutableList<T>实现原理.(上)

/玄魂

前言

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

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

2.1 概述

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

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

public static ImmutableList<T> Create<T>()

             {

                    return ImmutableList<T>.Empty;

             }

             public static ImmutableList<T> Create<T>(IEqualityComparer<T> equalityComparer)

             {

             return ImmutableList<T>.Empty.WithComparer(equalityComparer);

             }

             public static ImmutableList<T> Create<T>(T item)

             {

                    return ImmutableList<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>定义如下:

public interface IImmutableList<T> : IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable

      {

             IEqualityComparer<T> ValueComparer

             {

                    get;

             }

             IImmutableList<T> Clear();

             bool Contains(T value);

             int IndexOf(T value);

             IImmutableList<T> Add(T value);

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

             IImmutableList<T> Insert(int index, T element);

             IImmutableList<T> InsertRange(int index, IEnumerable<T> items);

             IImmutableList<T> Remove(T value);

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

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

             IImmutableList<T> RemoveRange(int index, int count);

             IImmutableList<T> RemoveAt(int index);

             IImmutableList<T> SetItem(int index, T value);

             IImmutableList<T> Replace(T oldValue, T newValue);

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

      }

IImmutableListQueries<T>定义如下:

      internal interface IImmutableListQueries<T>

      {

             int Count

             {

                    get;

             }

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

             void ForEach(Action<T> action);

             ImmutableList<T> GetRange(int index, int count);

             void CopyTo(T[] array);

             void CopyTo(T[] array, int arrayIndex);

             void CopyTo(int index, T[] array, int arrayIndex, int count);

             bool Exists(Predicate<T> match);

             T Find(Predicate<T> match);

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

             int FindIndex(Predicate<T> match);

             int FindIndex(int startIndex, Predicate<T> match);

             int FindIndex(int startIndex, int count, Predicate<T> match);

             T FindLast(Predicate<T> match);

             int FindLastIndex(Predicate<T> match);

             int FindLastIndex(int startIndex, Predicate<T> match);

             int FindLastIndex(int startIndex, int count, Predicate<T> match);

             int IndexOf(T item);

             int IndexOf(T item, int index);

             int IndexOf(T item, int index, int count);

             int LastIndexOf(T item);

             int LastIndexOf(T item, int index);

             int LastIndexOf(T item, int index, int count);

             bool TrueForAll(Predicate<T> match);

      }

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

SyncRootObject类型字段,作为同步锁对象。

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

ImmutableList<T>构造函数如下:

internal ImmutableList()

             {

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

                    this.valueComparer = EqualityComparer<T>.Default;

             }

             private ImmutableList(ImmutableList<T>.Node root, IEqualityComparer<T> valueComparer)

             {

                    root.Freeze();

                    this.root = root;

                    this.valueComparer = valueComparer;

             }

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

2.2 NODE

Node类继承自三个接口,

internal sealed class Node : IBinaryTree<T>, IEnumerable<T>, IEnumerable

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

interface IBinaryTree<out T>

      {

             int Height

             {

                    get;

             }

             T Value

             {

                    get;

             }

             IBinaryTree<T> Left

             {

                    get;

             }

             IBinaryTree<T> Right

             {

                    get;

             }

             bool IsEmpty

             {

                    get;

             }

             int Count

             {

                    get;

             }

      }

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

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

      internal static readonly Node EmptyNode = new  Node();

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

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

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

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

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

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

IsEmpty判断是否有子节点。

public bool IsEmpty

                    {

                           get

                           {

                                  return this.left == null;

                           }

                    }

rightleft就是左右子树。

2.3 行为

2.3.1    初始化

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

  static void Main(string[] args)

        {

 

            var fruitBasket = ImmutableList.Create<string>();

          var ass  = fruitBasket.Add("ddd");

        }

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

Create方法直接返回ImmutableList<T>.EmptyEmpty属性直接返回EmptySingleton

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

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

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

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

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

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

class TestCompare<T> : IEqualityComparer<T>

    {

        public bool Equals(T x, T y)

        {

            return x.Equals(y);

        }

        public int GetHashCode(T obj)

        {

            return this.GetHashCode();

        }

    }

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

   static void Main(string[] args)

        {

    var fruitBasket = ImmutableList.Create<string>(new TestCompare<string>());

          var ass  = fruitBasket.Add("ddd");

        }

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

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

      private ImmutableList(ImmutableList<T>.Node root, 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()方法,

internal void Freeze()

                    {

                           if (!this.frozen)

                           {

                                  this.left.Freeze();

                                  this.right.Freeze();

                                  this.frozen = true;

                           }

                    }

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

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

本篇博文到此结束,未完,待续。。。。。。


本文转自玄魂博客园博客,原文链接:http://www.cnblogs.com/xuanhun/archive/2013/05/06/3063787.html,如需转载请自行联系原作者

目录
相关文章
|
XML 存储 JSON
CocosCreator 面试题(十五)Cocos Creator如何内置protobuf JS版本?
CocosCreator 面试题(十五)Cocos Creator如何内置protobuf JS版本?
391 0
|
前端开发 芯片
【芯片前端】保持代码手感——握手协议ready打拍时序优化
【芯片前端】保持代码手感——握手协议ready打拍时序优化
407 0
|
C语言
实现一个简单的事件驱动处理框架
实现一个简单的事件驱动处理框架
248 0
|
5月前
|
测试技术 API 开发工具
Postman 对比 Swagger:您应该了解的关键区别
本文探讨了 Postman 和 Swagger 的主要特性和局限性,并推荐了为什么 Apifox 是更卓越的 API 文档工具。
|
5月前
|
数据采集 算法 数据安全/隐私保护
【硬件测试】基于FPGA的2ASK+帧同步系统开发与硬件片内测试,包含高斯信道,误码统计,可设置SNR
本文分享了基于FPGA的2ASK+帧同步系统硬件测试版本,包含ILA数据采集、VIO SNR设置及数据源模块。通过调整SNR(如45dB和10dB),实现对调制解调性能的验证。2ASK调制将数字信号转为二进制码,通过载波振幅变化传输;帧同步用于确定帧起始位置,确保数据正确解调。附带操作视频与核心Verilog代码,便于理解和复现。
150 9
|
10月前
|
机器学习/深度学习 算法
深入理解SVM中的核函数及其应用
深入理解SVM中的核函数及其应用
318 78
|
Java 测试技术 持续交付
自动化测试实践:从单元测试到集成测试
【6月更文挑战第28天】-单元测试:聚焦代码最小单元,确保每个函数或模块按预期工作。使用测试框架(如JUnit, unittest),编写覆盖所有功能和边界的测试用例,持续集成确保每次变更后自动测试。 - 集成测试:关注模块间交互,检查协同工作。选择集成策略,编写集成测试用例,模拟真实环境执行测试,整合到CI/CD流程以持续验证软件稳定性。 自动化测试提升软件质量,降低成本,加速开发周期,是现代软件开发不可或缺的部分。
|
9月前
|
供应链 监控 数据可视化
探索 Leangoo 在电商新品运营中的创新应用与价值
Leangoo 提供了一套全面高效的电商新品运营解决方案,涵盖项目规划、营销推广、供应链管理及数据分析等方面,通过任务卡、甘特图等工具实现跨部门协作与进度追踪,助力电商企业在竞争中脱颖而出。
探索 Leangoo 在电商新品运营中的创新应用与价值
|
10月前
|
域名解析 弹性计算 网络安全
CEN+私网NAT实现跨地域访问云服务需求-CEN企业版
本文介绍了如何通过企业版云企业网和私网NAT配置,实现ECS内网跨地域访问OSS资源的方法。该方法避免了跨地域配置云服务网段时可能出现的管控异常问题,适用于其他云服务如MQ等。
|
机器学习/深度学习 NoSQL Redis
Redis -- zset有序集合
Redis -- zset有序集合
282 0