Immutable Collections(3)Immutable List实现原理(中)变化中的不变

Immutable  Collections(3)Immutable List实现原理()变化中的不变

/玄魂

前言

在上一篇文章(Immutable Collections2ImmutableList<T>实现原理.(上),分析了)ImmutableList<T>的初始化过程,本篇博客分析除初始化之外的行为,当然概括起来也很简单——添加、删除、修改。这些行为的背后,我们会看到不可变集合的不变性是如何保持的,如何在不完全拷贝的情况下返回新的集合等等特性的秘密。

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

3.1  ADD

接上篇博文,当初始化一个没有任何元素的ImmutableList<T>对象之后,对象会获得一个EmptyNode

下面看看添加一个元素的流程,及数据结构的变化。

测试代码:

 static void Main(string[] args)

        {

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

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

 

如上图,在Add方法内部,会调用Node类型的Add方法,返回一个新的的Node实例。Add方法源码如下:

                    internal ImmutableList<T>.Node Add(T key)

                    {

                           return this.Insert(this.count, key);

                    }

Add方法又调用了Insert方法,此时count=0key=”ddd”。在Insert内部先判断了左子树是否为空,如果为空则创建新的Node,调用具有四个输入参数的构造函数。

      if (this.IsEmpty)//

                           {

                                  return new ImmutableList<T>.Node(key, thisthisfalse);

                           }

这一步很巧妙的完成了树的构造,代码如下:

private Node(T key, ImmutableList<T>.Node left, ImmutableList<T>.Node right, bool frozen = false)

                    {

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

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

                           this.key = key;

                           this.left = left;

                           this.right = right;

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

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

                           this.frozen = frozen;

                    }

原来的rootempty node)这里变成新Node的左右子节点,新节点key字段(即value)被赋值“ddd”,heightcount都等于1,此时frozen=false。需要注意的细节是,调用Node(T key, ImmutableList<T>.Node left, ImmutableList<T>.Node right, bool frozen = false)之前传入的this指针和函数内部的this指针指向的是不同的内存区域。

注意,传入的Node对象没有做任何修改,返回的是新NewNode

当前创建的Node对象的结构如下:

 

继续运行,从Node类里出来,回到ImmutableList<T>Add方法:

      public ImmutableList<T> Add(T value)

             {

                    ImmutableList<T>.Node node = this.root.Add(value);

                    return this.Wrap(node);

             }

在得到新的的Node后,会执行Wrap方法。

 

同理,内部的Node完成了树形结构的转换,外部的ImmutableList<T>也要完成这一转换,返回新的ImmutableList<T>对象,将新的Node赋值到自己的root字段上,并初始化相关字段。

      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;

             }

OK,终于又回到了Main函数中,完成了一次轮回:

 

ImmutableList<T>通过更新树结构,新建ImmutableList<T>对象同时更新对Node的引用创建新的集合。树结构虽然发生了变化,但是原来的集合对节点的引用并没有发生变化,从而保证了集合的不变性。

继续修改Main函数的代码:

  static void Main(string[] args)

        {

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

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

          var a3 = a2.Add("ccc");

        }

我们观察执行var a3 = a2.Add("ccc")时的行为变化。

 

当前代码沿着上图所示的路径再次来到Node类的Insert方法。

 

第一次执行Add时的情景上面分析过了,当Node的左右子树不为空时,首先要判断元素应该添加左还是右,判断逻辑很简单,判断当前准备添加元素的索引是否小于等于左子树元素的个数。由于当前左子树只有一个Empty节点,所以元素会被添加到右子树上去。可以设想一下,如果再次执行添加操作,元素还是会被添加到右子树上去,左边会一直为空。所以在每次添加操作执行完毕之后,会调用MakeBalanced方法来使左右平衡。如果您熟悉红黑树的话,对保持树的平衡时使用的扭转算法应该不会陌生。这里我不想深入解释ImmutableList 的“平衡扭转”算法,我觉得单独拿出来一篇博文来讲解会更好。

下一篇博客中,我们继续分析ImmutableList的其他行为的原理。

转载于:https://www.cnblogs.com/xuanhun/p/3159823.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/460984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大话Fragment管理

大话Fragment管理 上一个项目遇到了一个Activity 管理30个Fragment的情况&#xff0c;刚开始的时候真的管理的焦头烂额&#xff0c;但是后来不停的研究api文档&#xff0c;渐渐的明白了android的Fragment管理 体系。下面用…

c语言例题功能作用,一篇C语言面试题的汇总

2015-03-21 06:30:02阅读( 107 )1&#xff0e; 找错void test1(){char string[10]; //string的长度应该设为11&#xff0c;要给”留出位置char* str1”0123456789″;strcpy( string, str1);}void test1(){char string[10], str1[10];for(I0; I<10&#xff1b; I ) //变量…

第一季4:Hi3518E_SDK_Vx.x.x.x的SDK目录结构

一、Hi3518E_SDK_V1.0.3.0.tgz的位置 “Hi3518E_SDK_V1.0.3.0.tgz”位于“Hi3518E V200R001C01SPC030”中&#xff0c;其目录包含关系如下。 Hi3518E_SDK_V1.0.3.0.tgz 解压后内部文件组织如下。 二、执行SDK展开脚本sdk.unpack后的目录关系 将Hi3518E_SDK_V1.0.3.0.tgz拷贝到…

第一季6:海思方案中uboot、kernel和rootfs的烧写方法

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、概述 因为所用的板子默认从SPI Flash启动&#xff0c;因此本文主要讲如何“使用tftp&#xff0c;烧写映像文件&#xff08;uboot、kernel、rootfs&#xff09;到SPI Flash”。另外海思还提供了“…

CSS如何正确显示人民币符号¥

我们做网页时要正确显示人民币符号可以用如下办法&#xff1a;CSS&#xff1a;在中文输入法下用shift4输出的&#xffe5;在微软雅黑&#xff08;Microsoft YaHei&#xff09;&#xff0c;华文细黑&#xff08;STXihei&#xff09;&#xff0c;&#xff08;MingLiu&#xff09;…

java模式之装饰模式

1. 什么叫装饰模式? 根据业务的需求&#xff0c;需要对一个类的方法进行增强的处理。 2. 为什么需要装饰模式&#xff1f; 拓展性更加的好&#xff0c;当觉得这个装饰不好的时候&#xff0c;可以直接拿下&#xff0c;不需要改变任何的代码。 3. 装饰模式的一个具体的应用&…

字体文件解析 c语言,如何正确地从C语言的文件中读取某些字符串?

您试图从文件中读取的内容并不简单,但可以通过设置一个标志来处理,该标志告诉您是否已经看到a或b,跳过所有空白和:字符,将所有其他字符存储在缓冲区中,根据需要重新分配,然后在第二个“A”或“B”找到了,把那个角色放回FILE*流与ungetc,nul终止并返回缓冲区。听起来很简单——对…

如何判断linux系统是32位还是64位的?

最简单的是输入命令&#xff1a;uname -m 查看输出&#xff0c;如果输出是i686这种ixxx系列的&#xff0c;都是32位系统&#xff0c;如果出现x86_64这样的&#xff0c;就是64位。

02 - 替换SetInput方法 VTK 6.0 迁移 (2013-06-30 16:22)

VTK6 引入了许多不兼容的变化&#xff0c;这其中就包括用SetInputData()和SetInputConnection()替换SetInput()方法。在先前的版本中&#xff0c;VTK4 引入了SetInput()方法&#xff0c;VTK5中引入了SetInputConnection()。 下面举一些例子&#xff1a;在如下例子中&#xff0c…

c语言中怎么 写子程序,哪位师傅知道51单片机怎样编写子程序?C语言的。在主程序里调...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼/*************************************************************************************** 外部中断0实验 *实现现象&#xff1a;下载程序后按下K3按键可以对D1小灯状态取反。注意事项&#xff1a;无。***********************…

Java Web笔记之Struts2.1 +Hibernate3.3 +Spring3.0

2019独角兽企业重金招聘Python工程师标准>>> 1、Struts2 1.1、了解Struts2 Struts2是基于MVC设计模式的Java Web框架技术之一&#xff0c;按照MVC设计思想把Java Web应用程序分为&#xff1a; 控制器层&#xff0c;包括核心控制器FilterDispatcher和业务控制器Actio…

高地址和低地址、高字节与低字节、大小端模式的转换、存储顺序

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 一、高地址和低地址 二、高字节低字节 如int a16777220&#xff0c;化为十六进制是0x01 00 00 04&#xff0c;则04属于低字节&#xff0c;01属于高字节。 三、大小端模式 &#xff08;1&#xff09;如果a在内…

漫谈数据挖掘从入门到进阶

入门&#xff1a;数据挖掘入门的书籍&#xff0c;中文的大体有这些&#xff1a;Jiawei Han的《数据挖掘概念与技术》Ian H. Witten / Eibe Frank的《数据挖掘 实用机器学习技术》Tom Mitchell的《机器学习》TOBY SEGARAN的《集体智慧编程》Anand Rajaraman的《大数据》Pang-Nin…

C语言程序设计二期末考试,C语言程序设计期末考试试卷2.doc

C语言程序设计期末考试试卷2选择题(单选题&#xff0c;每小题2分&#xff0c;共15题&#xff0c;30分)注意&#xff1a;请将答案写在每小题的题号左边1&#xff0e;一个C程序由若干个C函数组成&#xff0c;各个函数在文件中的位置为_____A______。A) 任意B) 第一个函数必须是主…

(转)Asp.Net生命周期系列一

原文地址&#xff1a;http://www.cnblogs.com/skm-blog/archive/2013/07/07/3176713.html Asp.Net生命周期对于初级甚至中级程序员来说&#xff0c;一直都是一个难题&#xff0c;很多程序员不了解生命周期&#xff0c;导致使用Asp.Net做开发感觉很不灵活&#xff0c;感觉太多东…

c语言枚举法礼泡声次数,C语言枚举类型举例

C语言枚举类型举例注&#xff1a;以下全部代码的执行环境为VC 6.0宏和枚举的区别宏和枚举之间的差别主要在作用的时期和存储的形式不同&#xff0c;宏是在预处理的阶段进行替换工作的&#xff0c;它替换代码段的文本&#xff0c;程序运行的过程中宏已不存在了。而枚举是在程序运…

【IOS】集成zxing(二维码扫描)

现在zxing已经到了2.2版本&#xff0c;以前的集成方式出了点问题。下面我做出一点修正。以前的版本的集成方法&#xff0c;参考&#xff1a;http://blog.devtang.com/blog/2012/12/23/use-zxing-library/按照以前的方式做好后 然后就是适配以下现在的版本的修改1.增加 SenTe…

与TCP有关的面试内容

以下内容源于网络资料的学习与整理。 参考博客 TCP/IP四层模型 - BlueTzar - 博客园&#xff08;OSI参考模型和TCP模型的详解&#xff0c;包括格式&#xff09; TCP/IP协议-为什么说TCP是可靠连接_shuaixio的博客-CSDN博客&#xff08;为什么可靠及优缺点&#xff09; 两张动图…

登录后跳转到原页面

可以在点击登录页面时&#xff0c;进行url传值&#xff0c;比如从a.html到b.html,可以a.html?oldurlb.html 带有特殊符号、中文的可以加个encodeURIComponent()转载于:https://www.cnblogs.com/wuchao/p/3179350.html