Immutable Collections(2)ImmutableList 實現原理.(上)


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方法,我將在下一篇博文中單獨分析。

本篇博文到此結束,未完,待續。。。。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM