執行緒安全集合-不可變的集合

by vivid 24. 二月 2016 05:00

.NET Magazine國際中文電子雜誌
者:許薰尹
稿:張智凱
文章編號:N160216802
出刊日期:2016/2/24
開發工具:Visual Studio 2015 Enterprise
版本:.NET Framework 4.6

.NET Framework 4.5版新增一些不可變的集合物件,特別適用於同步或非同步的情況下使用。當集合中的內容不常變動,且想要以Thread Safe的方式操作集合時,便可以使用定義在 System.Collections.Immutable命名空間下的集合類別,由於它們是不可變的集合,只提供唯讀的存取機制,因此更節省記憶體的使用空間。本篇文章將介紹這些不可變的集合。

使用 Visual Studio 2015建立的範本專案中,預設並沒有參考包含這些類別的組件,因此你需要先利用Nuget工具下載它們。我們以Console專案為例子,使用Nuget下載System.Collections.Immutable套件:

Install-Package System.Collections.Immutable

這個命名空間下包含以下常用型別:

  • ImmutableArray與ImmutableArray<T>
  • ImmutableDictionary與ImmutableDictionary<TKey , TValue>
  • ImmutableSortedDictionary與ImmutableSortedDictionary<TKey , TValue>
  • ImmutableHashSet 與ImmutableHashSet<T>
  • ImmutableSortedSet與ImmutableSortedSet<T>
  • ImmutableList與ImmutableList<T>
  • ImmutableStack 與ImmutableStack<T>
  • ImmutableQueue與ImmutableQueue<T>

這些都屬於執行緒安全的集合(Threadsafe Collections)。

 

ImmutableArray與ImmutableArray<T>

以下程式碼使用ImmutableArray <T>建立存放int型別的集合,叫用Add方法新增三個項目到陣列之中,然後利用迴圈,叫用RemoveAt方法將所有項目印出之後,將此項目從集合中移除:

using System;
using System.Linq;
using System.Collections.Immutable;
namespace ConsoleApplication2 {
  class Program {
    static void Main( string [ ] args ) {
      ImmutableArray<int> ary = ImmutableArray<int>.Empty;
      ary = ary.Add( 200 );
      ary = ary.Add( 100 );
      ary = ary.Add( 300 );

      var ary2 = ary.Add( 400 );

      Console.WriteLine( "ary" );
      while ( ary.Count( ) > 0 ) {
        Console.WriteLine( "\t" + ary [ 0 ] ); //印出200 100 300
        ary = ary.RemoveAt( 0 );
      }
      Console.WriteLine( "ary Count : {0} " , ary.Count( ) ); //0
      Console.WriteLine( );
      Console.WriteLine( "ary 2" );
      while ( ary2.Count( ) > 0 ) {
        Console.WriteLine( "\t" + ary2 [ 0 ] ); //印出200 100 300 400
        ary2 = ary2.RemoveAt( 0 );
      }
      Console.WriteLine( "ary2 Count : {0} " , ary2.Count( ) ); //0
    }
  }
}


程式碼中,利用Add 、RemoveAt方法新增、或移除ary陣列中的項目時,都會回傳變動過的集合,更新ary變數,原始的集合參考並不會變更,因此範例中讓ary2參照到ary陣列,此時ary與ary2兩個陣列會共用「200」、「100」、「300」項目佔有的記憶體,來增進效能。這個範例的執行結果,請參考下圖所示:

clip_image002

圖 1

所有不可變的集合(Immutable Collection)都具有以下的特色:

  • 不可變的集合之實體不會變動,因此它具備執行緒安全的特色。
  • 當你呼叫不可變集合的方法,原有集合不變動,方法將回傳一個異動的集合。
  • 不可變的集合適合用在共享狀態。

ImmutableDictionary與ImmutableDictionary<TKey , TValue>

ImmutableDictionary與ImmutableDictionary<TKey , TValue>集合中的項目,都是由一組鍵/值(Key/Value)配對項目所成的。ImmutableDictionary<TKey , TValue>的鍵值可以是任意型別。參考以下範例程式碼:

using System;
using System.Collections.Immutable;
namespace ConsoleApplication2 {
  class Program {
    static void Main( string [ ] args ) {
      ImmutableDictionary<string , string> dics = ImmutableDictionary<string , string>.Empty;

      dics = dics.Add( "A01" , "Mary" );
      dics = dics.Add( "B01" , "Candy" );
      dics = dics.Add( "C01" , "LiLi" );

      Console.WriteLine( "Count = {0}" , dics.Count ); //3

      foreach ( var item in dics ) {
        Console.WriteLine( "Key : {0} , Value :{1} " , item.Key , item.Value );
        dics = dics.Remove( item.Key );
      }
      Console.WriteLine( "Count = {0}" , dics.Count ); //0

      Console.WriteLine( );
    }
  }
}


 

這個範例的執行結果,請參考下圖所示:

clip_image004

圖 2

ImmutableSortedDictionary與ImmutableSortedDictionary<TKey , TValue>

ImmutableSortedDictionary與ImmutableSortedDictionary<TKey , TValue>和這兩個類別相似,其中的項目會自動排序,例如修改上例程式碼,改用ImmutableSortedDictionary<string , string>:

 

using System;
using System.Collections.Immutable;
namespace ConsoleApplication2 {
  class Program {
    static void Main( string [ ] args ) {
      ImmutableSortedDictionary<string , string> dics = ImmutableSortedDictionary<string , string>.Empty;

      dics = dics.Add( "A01" , "Mary" );
      dics = dics.Add( "B01" , "Candy" );
      dics = dics.Add( "C01" , "LiLi" );

      Console.WriteLine( "Count = {0}" , dics.Count ); //3

      foreach ( var item in dics ) {
        Console.WriteLine( "Key : {0} , Value :{1} " , item.Key , item.Value );
        dics = dics.Remove( item.Key );
      }
      Console.WriteLine( "Count = {0}" , dics.Count ); //0

      Console.WriteLine( );
    }
  }
}

這個範例的執行結果,請參考下圖所示:

clip_image006

圖 3

ImmutableHashSet 與ImmutableHashSet<T>

ImmutableHashSet 與ImmutableHashSet<T>適合用在儲存不重複的項目,在不常變動的情況下,可以讓多執行緒安全地存取。以下範例叫用六行Add方法新增6個項目到集合中,ImmutableHashSet會自動保留唯一項目,因此實際上放到集合中的項目只有3個:

using System;
using System.Collections.Immutable;
namespace ConsoleApplication2 {
  class Program {
    static void Main( string [ ] args ) {
      ImmutableHashSet<int> set = ImmutableHashSet<int>.Empty;
      set = set.Add( 100 );
      set = set.Add( 300 );
      set = set.Add( 200 );
      set = set.Add( 100 );
      set = set.Add( 300 );
      set = set.Add( 200 );

      Console.WriteLine("Count = {0}", set.Count ); //3

      foreach ( var item in set ) {
        Console.WriteLine( item );
        set = set.Remove( item );
      }
      Console.WriteLine( "Count = {0}" , set.Count ); //0
    }
  }
}

 

這個範例的執行結果,請參考下圖所示:

clip_image008

圖 4

ImmutableSortedSet與ImmutableSortedSet<T>

ImmutableSortedSet、ImmutableSortedSet<T>與ImmutableHashSet 與ImmutableHashSet<T>非常類似,只多一個功能,ImmutableSortedSet、ImmutableSortedSet<T>會排序其中的項目,參考以下範例程式碼:

using System;
using System.Collections.Immutable;
namespace ConsoleApplication2 {
  class Program {
    static void Main( string [ ] args ) {
      ImmutableSortedSet<int> set = ImmutableSortedSet<int>.Empty;
      set = set.Add( 100 );
      set = set.Add( 300 );
      set = set.Add( 200 );
      set = set.Add( 100 );
      set = set.Add( 300 );
      set = set.Add( 200 );
      Console.WriteLine( "Count = {0}" , set.Count ); //3

      foreach ( var item in set ) {
        Console.WriteLine( item );
        set = set.Remove( item );
      }
      Console.WriteLine( "Count = {0}" , set.Count ); //0

    }
  }
}

 

ImmutableSortedSet<T>會排序集合中的內容,這個範例的執行結果,請參考下圖所示:

clip_image010

圖 5

ImmutableList與ImmutableList<T>

ImmutableList與ImmutableList<T>可以利用索引存取集合中的項目,參考以下範例程式碼,使用Add方法新增一個項目到集合;使用AddRange新增一個陣列資料到集合。此外,還可以利用Insert方法,可以插入項目到指定的索引位置,索引以0開始計,參考以下範例程式碼:

using System;
using System.Linq;
using System.Collections.Immutable;
namespace ConsoleApplication2 {
  class Program {
    static void Main( string [ ] args ) {
      ImmutableList<int> list = ImmutableList<int>.Empty;
      list = list.Add( 100 );
      list = list.AddRange( new int [ ] { 200 , 300 } );
      list = list.Insert( 0 , 400 );

      foreach ( var item in list ) {
        Console.WriteLine(item);
      }
    }
  }
}


 

這個範例的執行結果,請參考下圖所示:

clip_image012

圖 6

ImmutableList與ImmutableList<T>內部使用二元樹(Binary Tree)方式儲存資料,就效能上而言,應該儘量使用foreach來存取其中的項目,若使用一般的for迴圈雖然依舊可以將其中的項目取出,但效能會比foreach慢很多。

ImmutableStack與ImmutableStack<T>

堆疊是後進先出(last-in, first-out)的資料結構,以下程式碼使用ImmutableStack.Create<T>建立存放物件型別項目的堆疊,叫用Push方法新增三個項目到堆疊之中,然後叫用Pop方法取出三個項目:

using System;
using System.Linq;
using System.Collections.Immutable;
namespace ConsoleApplication2 {
  class Program {
    static void Main( string [ ] args ) {
      ImmutableStack<object> stack = ImmutableStack.Create<object>( );

      stack = stack.Push( "mary" );
      stack = stack.Push( 1 );
      stack = stack.Push( DateTime.Now );

      while ( stack.Count( ) > 0 ) {
        object item;
        stack = stack.Pop( out item );
        Console.WriteLine( item );
      }
    }
  }
}

這個範例的執行結果,請參考下圖所示:

clip_image014

圖 7

以下程式碼建立存放int型別項目的堆疊,叫用Push方法新增三個數值型別的項目到堆疊之中,然後叫用Pop方法取出三個項目:

 

using System;
using System.Linq;
using System.Collections.Immutable;
namespace ConsoleApplication2 {
  class Program {
    static void Main( string [ ] args ) {
      ImmutableQueue<int> q = ImmutableQueue<int>.Empty;
      q = q.Enqueue( 100 );
      q = q.Enqueue( 200 );
      q = q.Enqueue( 300 );

      while ( q.Count( ) > 0 ) {
        int item;
        q = q.Dequeue( out item );
        Console.WriteLine( item );
      }
    }
  }
}


這個範例的執行結果,請參考下圖所示:

clip_image016

圖 8

和之前的陣列例子一樣,參考以下範例程式碼,每次呼叫Push方法,新增一個項目到堆疊,就回傳變動過的集合,更新stack變數,原始的集合參考並不會變更,因此範例中讓stack2參照到stack堆疊,stack與stack2兩個堆疊會共用「200」、「100」、「300」項目佔有的記憶體,來增進效能。

using System;
using System.Linq;
using System.Collections.Immutable;
namespace ConsoleApplication2 {
  class Program {
    static void Main( string [ ] args ) {

      ImmutableStack<int> stack = ImmutableStack<int>.Empty;
      stack = stack.Push( 200 );
      stack = stack.Push( 100 );
      stack = stack.Push( 300 );

      var stack2 = stack.Push( 400 );

      Console.WriteLine( "Stack" );
      while ( stack.Count( ) > 0 ) {
        int item;
        stack = stack.Pop( out item );
        Console.WriteLine( "\t{0}" , item );
      }

      Console.WriteLine( "Stack 2" );
      while ( stack2.Count( ) > 0 ) {
        int item;
        stack2 = stack2.Pop( out item );
        Console.WriteLine( "\t{0}" , item );
      }
   }

  }
}

 

 

這個範例的執行結果,請參考下圖所示:

clip_image018

圖 9

ImmutableQueue與ImmutableQueue<T>

佇列和堆疊類似,但它是屬於先進先出(first-in, first-out)的資料結構,以下範例程式碼展現如何使用不可變的佇列:

using System;
using System.Linq;
using System.Collections.Immutable;
namespace ConsoleApplication2 {
  class Program {
    static void Main( string [ ] args ) {
      ImmutableQueue<int> q = ImmutableQueue<int>.Empty;
      q = q.Enqueue( 100 );
      q = q.Enqueue( 200 );
      q = q.Enqueue( 300 );

      while ( q.Count( ) > 0 ) {
        int item;
        q = q.Dequeue( out item );
        Console.WriteLine( item );
      }
    }
  }
}


這個範例的執行結果,請參考下圖所示:

clip_image020

圖 10

Tags:

.NET Magazine國際中文電子雜誌 | .NET Framework | C# | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List