{"id":893,"date":"2013-06-24T09:19:00","date_gmt":"2013-06-24T09:19:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/dotnet\/2013\/06\/24\/please-welcome-immutablearrayt\/"},"modified":"2021-10-04T12:28:55","modified_gmt":"2021-10-04T19:28:55","slug":"please-welcome-immutablearrayt","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/please-welcome-immutablearrayt\/","title":{"rendered":"Please welcome ImmutableArray"},"content":{"rendered":"<p>We&rsquo;ve just released an update to our <a href=\"https:\/\/nuget.org\/packages\/Microsoft.Bcl.Immutable\/\">immutable collection package<\/a> which adds a new member to the family of immutable collection types: <strong>ImmutableArray&lt;T&gt;<\/strong>.<\/p>\n<p>In this post, I&rsquo;ll talk about why we added another collection and how it relates to the existing types. I&rsquo;ll also cover some minor updates we did to our package.<\/p>\n<h2>Introduction ImmutableArray&lt;T&gt;<\/h2>\n<p>Sometimes a little bit of code says more than a thousand pictures, so let&rsquo;s look at the declaration of immutable array:<\/p>\n<pre>        public struct ImmutableArray&lt;T&gt; : IList,\n                                          IList&lt;T&gt;,\n                                          IReadOnlyList&lt;T&gt;,\n                                          IImmutableList&lt;T&gt;,\n                                          IEquatable&lt;ImmutableArray&lt;t&gt;&gt;,\n                                          IStructuralComparable,\n                                          IStructuralEquatable\n        {\n            \/\/ ...\n        }<\/pre>\n<p>As you can see <strong>ImmutableArray&lt;T&gt;<\/strong> implements <strong>IImmutableList&lt;T&gt;<\/strong> which begs the question how it is different from the existing implementation <strong>ImmutableList&lt;T&gt;<\/strong>.<\/p>\n<p>The answer: performance.<\/p>\n<p>Arrays are hard to beat in several ways: they provide an O(1) element access, they are very cache friendly as all data is co-located, and they provide low overhead for small collections (&lt; 16 elements).<\/p>\n<p><strong>ImmutableArray&lt;T&gt;<\/strong> is a very thin wrapper around a regular array and thus shares all the benefits with them. We even made it a value type (struct) as it only has a single field which holds the array it wraps. This makes the size of the value type identical to the reference of the array. In other words: passing around an immutable array is as cheap as passing around the underlying array. Since it&rsquo;s a value type, there is also no additional object allocation necessary to create the immutable wrapper, which can reduce GC pressure.<\/p>\n<p>The key operations on <strong>ImmutableArray&lt;T&gt;<\/strong> just forward to the underlying array, including the indexer. In contrast to <strong>List&lt;T&gt;<\/strong> or <strong>ArraySegment&lt;T&gt;<\/strong> the underlying array always has the same length as the outer type, i.e. the immutable array. This means we can avoid having additional bounds checking and just rely on the underlying array. This allows the CLR code generation to inline the indexer access.<\/p>\n<p>We&rsquo;ve also implemented custom LINQ operators for <strong>ImmutableArray&lt;T&gt;<\/strong>. This avoids boxing immutable arrays into an <strong>IEnumerable&lt;T&gt;<\/strong>.<\/p>\n<h2>Using ImmutableArray&lt;T&gt;<\/h2>\n<p>Creating an immutable array is similar to creating an immutable list, i.e. it follows the factory pattern via static <strong>Create<\/strong> methods:<\/p>\n<pre>        ImmutableArray&lt;int&gt; array = ImmutableArray.Create(1, 2, 3);<\/pre>\n<p>You can also create an immutable array via the <strong>ToImmutableArray()<\/strong> extension method:<\/p>\n<pre>        IEnumerable&lt;int&gt; someInts = Enumerable.Range(1, 100);\n        ImmutableArray&lt;int&gt; array = someInts.ToImmutableArray();<\/pre>\n<p>It also supports the builder pattern which allows constructing immutable arrays via mutation:<\/p>\n<pre>        ImmutableArray&lt;int&gt;.Builder builder = ImmutableArray.CreateBuilder&lt;int&gt;();\n        builder.Add(1);\n        builder.Add(2);\n        builder.Add(3);\n        ImmutableArray&lt;int&gt; oneTwoThree = builder.ToImmutable();<\/pre>\n<p>You may wonder how using the builder differs from using a <strong>List&lt;T&gt;<\/strong> with the <strong>ToImmutableArray()<\/strong> extension method. Generally, all immutable collection builders are functionally equivalent to their ordinary mutable collection types (<strong>List&lt;T&gt;<\/strong>, <strong>Dictionary&lt;TKey, TValue&gt;<\/strong> etc). They are purely there to provide better performance. Using the <strong>ImmutableArray&lt;T&gt;<\/strong> builder can improve performance as the implementation uses a trick to avoid type checks. Normally the CLR has to perform additional type checks at runtime to ensure that storing an element in an array is type safe, because arrays are covariant which means the correctness can&rsquo;t be easily checked statically. <strong>ImmutableArray&lt;T&gt;<\/strong> avoids this by wrapping references in a pointer-sized value type. For more details on this subject I recommend <a href=\"http:\/\/blogs.msdn.com\/b\/ericlippert\/archive\/2007\/10\/17\/covariance-and-contravariance-in-c-part-two-array-covariance.aspx\">this blog post<\/a> from Eric Lippert.<\/p>\n<p>Reading data from an <strong>ImmutableArray&lt;T&gt;<\/strong> is similar to reading regular arrays:<\/p>\n<pre>        ImmutableArray&lt;int&gt; array = \/\/...\n        for (var i = 0; i &lt; array.Length; i++)\n        {\n            Console.WriteLine(array[i]);\n        }<\/pre>\n<p>We&rsquo;ve decided to go with having a <strong>Length<\/strong> property instead of <strong>Count<\/strong> because we see <strong>ImmutableArray&lt;T&gt;<\/strong> as a replacement for regular arrays. This makes it easier to port existing code. It also makes the design a bit more self-contained.<\/p>\n<p>Of course we also support enumerating the elements via <strong>foreach<\/strong>. <strong>ImmutableArray&lt;T&gt;<\/strong> follows what other collection types do as well: it implements<strong> IEnumerable&lt;T&gt;<\/strong> but also provides a custom enumerator which allows the compiler to generate more efficient code which avoids boxing the enumerator.<\/p>\n<p>Since <strong>ImmutableArray&lt;T&gt;<\/strong> is a value type, it overloads the equals (==) and not-equals (!=) operators. They are defined as using reference equality on the underlying array.<\/p>\n<p>The default value of <strong>ImmutableArray&lt;T&gt;<\/strong> has the underlying array initialized with a null reference. In this case it behaves the same way as an <strong>ImmutableArray&lt;T&gt;<\/strong> that has been initialized with an empty array, i.e. the <strong>Length<\/strong> property returns 0 and iterating over it simply doesn&rsquo;t yield any values. In most cases this is the behavior you would expect. However, in some cases you may want to know that the underlying array hasn&rsquo;t been initialized yet. For that reason <strong>ImmutableArray&lt;T&gt;<\/strong> provides the property <strong>IsDefault<\/strong> which returns true if the underlying array is a null reference. For example you can use that information to implement lazy initialization:<\/p>\n<pre>        private ImmutableArray&lt;byte&gt; _rawData;\n        public ImmutableArray&lt;byte&gt; RawData\n        {\n            get\n            {\n                if (_rawData.IsDefault)\n                    _rawData = LoadData();\n                return _rawData;\n            }\n        }<\/pre>\n<h2>ImmutableArray&lt;T&gt; vs. ImmutableList&lt;T&gt;<\/h2>\n<p>In contrast to what <a href=\"http:\/\/blogs.msdn.com\/b\/bclteam\/archive\/2013\/03\/06\/update-to-immutable-collections.aspx\">I said earlier<\/a>, we decided to make immutable array a persistent data structure. In other words: similar to <strong>ImmutableList&lt;T&gt;<\/strong> it has methods that allow creating new instances of immutable arrays with different contents. Mind you, the performance characteristic of those operations is fairly different from <strong>ImmutableList&lt;T&gt;<\/strong>. <strong>ImmutabeList&lt;T&gt;<\/strong> uses a tree data structure that allows sharing. It also allows for making sure updates can be done in O(log n).<\/p>\n<p>The following table summarizes the performance characteristics of <strong>ImmutableArray&lt;T&gt;<\/strong>. You can find a similar table for the existing types <a href=\"http:\/\/blogs.msdn.com\/b\/andrewarnottms\/archive\/2013\/01\/07\/immutable-collection-algorithmic-complexity.aspx\">here<\/a>.<\/p>\n<table border=\"1\">\n<thead>\n<tr>\n<th>Operation<\/th>\n<th>ImmutableArray&lt;T&gt; Complexity<\/th>\n<th>ImmutabeList&lt;T&gt; Complexity<\/th>\n<th>Comments<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<th>Create()<\/th>\n<td>O(n)<\/td>\n<td>O(n)<\/td>\n<td>Requires to copy the input array to ensure the underlying array can&rsquo;t be modified<\/td>\n<\/tr>\n<tr>\n<th>this[]<\/th>\n<td>O(1)<\/td>\n<td>O(log n)<\/td>\n<td>Directly index into the underlying array<\/td>\n<\/tr>\n<tr>\n<th>Add()<\/th>\n<td>O(n)<\/td>\n<td>O(log n)<\/td>\n<td>Requires creating a new array<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Reasons to use immutable array:<\/p>\n<ul>\n<li>Updating the data is rare or the number of elements is quite small (&lt;16)<\/li>\n<li>you need to be able to iterate over the data in performance critical sections<\/li>\n<li>you have many instances of immutable collections and you can&rsquo;t afford keeping the data in trees<\/li>\n<\/ul>\n<p>Reasons to stick with immutable list:<\/p>\n<ul>\n<li>Updating the data is common or the number of elements isn&rsquo;t expected to be small<\/li>\n<li>Updating the collection is more performance critical than iterating the contents<\/li>\n<\/ul>\n<p>In general, when all you need is an immutable array and you don&rsquo;t plan on changing the data ever, use <strong>ImmutableArray&lt;T&gt;<\/strong>. If you need to update the data, use <strong>ImmutableList&lt;T&gt;<\/strong>.<\/p>\n<p>If you do update the data but think <strong>ImmutableArray&lt;T&gt;<\/strong> could perform better overall, you should try both and measure. Remember that designing for performance means to consider different trade-offs. It&rsquo;s key to measure those in actual scenarios, under real-world workloads.<\/p>\n<p>What does this mean for code that operates with the interface <strong>IImmutableList&lt;T&gt;<\/strong>? The interface could be backed by either an immutable list or an immutable array (or a custom type). Due to the different complexities the code cannot rely on the exact complexity. So in general the code should use bulk operations whenever possible in order to make sure the cost is minimized. For example, instead of calling <strong>Add()<\/strong> in a loop you should prefer a single call to <strong>AddRange()<\/strong>.<\/p>\n<h2>Other Changes in this Update<\/h2>\n<h2>Create() and From() factory methods<\/h2>\n<p>In the last release, <a href=\"http:\/\/blogs.msdn.com\/b\/bclteam\/archive\/2013\/03\/06\/update-to-immutable-collections.aspx\">we added<\/a> the <strong>Create()<\/strong> factory methods for constructing immutable collections. We created overloads for both scalars as well as collections:<\/p>\n<pre>        public static ImmutableList&lt;T&gt; Create&lt;T&gt;();\n        public static ImmutableList&lt;T&gt; Create&lt;T&gt;(params T[] items);\n        public static ImmutableList&lt;T&gt; Create&lt;T&gt;(IEnumerable&lt;T&gt; items);<\/pre>\n<p>We discovered that the overload that takes the <strong>IEnumerable&lt;T&gt;<\/strong> can have surprising results. You&rsquo;d think you can use the overload that takes <strong>IEnumerable&lt;T&gt;<\/strong> by creating collections from other collections:<\/p>\n<pre>        var list = new List&lt;string&gt;();\n        list.Add(\"One\");\n        list.Add(\"Two\");\n        \/\/ Doh! Actually I wanted to get ImmutableList&lt;string&gt;\n        ImmutableList&lt;list&lt;string&gt;&gt; il = ImmutableList.Create(list);<\/pre>\n<p>Instead of creating an <strong>ImmutableList&lt;string&gt;<\/strong> you end up creating an <strong>ImmutableList&lt;List&lt;string&gt;&gt;<\/strong> because overload resolution prefers the <strong>params<\/strong> overload over an implicit conversion from<strong> List&lt;string&gt;<\/strong> to <strong>IEnumerable&lt;string&gt;<\/strong>.<\/p>\n<p>For that reason we&rsquo;ve decided to remove the ambiguity by renaming all factory methods that operate over <strong>IEnumerable&lt;T&gt;<\/strong> to <strong>From<\/strong>:<\/p>\n<pre>        public static ImmutableList&lt;T&gt; Create&lt;T&gt;();\n        public static ImmutableList&lt;T&gt; Create&lt;T&gt;(params T[] items);\n        public static ImmutableList&lt;T&gt; From&lt;T&gt;(IEnumerable&lt;T&gt; items);<\/pre>\n<p>We decided not to rename the <strong>params<\/strong> overload as it is usually called with one or more scalars and not with an array. We believe this design will appear more consistent from typical call sites.<\/p>\n<h2>CreateBuilder() factory methods<\/h2>\n<p>Previously, you couldn&rsquo;t create builders directly. You had to create them from an immutable collection like this:<\/p>\n<pre>        ImmutableList&lt;int&gt;.Builder builder = ImmutableList.Create&lt;int&gt;().ToBuilder(); <\/pre>\n<p>Now you can simply call a factory method, similar to creating an immutable collection itself:<\/p>\n<pre>        ImmutableList&lt;int&gt;.Builder builder = ImmutableList.CreateBuilder&lt;int&gt;();<\/pre>\n<h2>Value Comparers<\/h2>\n<p>Our original design of <strong>IImmutableList&lt;T&gt;<\/strong> was based on the idea of keeping it aligned with <strong>IImmutableDictionary&lt;TKey, TValue&gt;<\/strong> and <strong>IImmutableSet&lt;T&gt;<\/strong>. Both of them have a built-in notion of comparers. When implementing <strong>IImmutableList&lt;T&gt;<\/strong> on <strong>ImmutableArray&lt;T&gt;<\/strong>, we were faced with the problem of supporting a custom comparer on the type. We could have added one more field to store it, but in this case <strong>ImmutableArray&lt;T&gt;<\/strong> would no longer be a cheap wrapper around an array. So we decided to drop the idea of storing a customer comparer on the list itself and instead require consumers to pass in the equality comparer they want to use.<\/p>\n<p>As a result we removed the <strong>ValueComparer<\/strong> property and <strong>WithComparer<\/strong> methods from <strong>IImmutableList&lt;T&gt;<\/strong>.<\/p>\n<p>For the members that implicitly used the comparer we&rsquo;ve changed their signature to take a comparer (for example, the <strong>IndexOf<\/strong> method). This is a breaking change for implementers. For consumers, most of the signature changes shouldn&rsquo;t be source breaking as we also added extension methods that pass in the default comparer.<\/p>\n<h2>TryGetValue() for sets<\/h2>\n<p>We&rsquo;ve received the feedback that <strong>IImmutableSet&lt;T&gt;<\/strong> doesn&rsquo;t have a way to get the actual value that is stored in the set &ndash; it only allows for checking whether it contains a value that is considered equal. In most cases, that&rsquo;s not a problem at all. But consider a set of strings that uses a case-insensitive comparer, for example a set of filenames. If you want to know for a given filename what the canonical casing is, you are out of luck. Therefore we added a <strong>TryGetValue<\/strong> method that allows retrieving the original value that was added to the set:<\/p>\n<pre>        var set = ImmutableHashSet.Create&lt;string&gt;(StringComparer.OrdinalIgnoreCase);\n        set = set.Add(\"D:\\Src\\Test.cs\");\n        string original;\n        if (set.TryGetValue(\"d:\\src\\test.cs\", out original))\n        {\n            \/\/ original contains \"D:\\Src\\Test.cs\"\n        }<\/pre>\n<h2>GetValueOrDefault() for dictionaries<\/h2>\n<p>Lastly, we&rsquo;ve fixed an issue with the&nbsp;<strong>GetValueOrDefault()<\/strong> extension method we&rsquo;ve added for dictionaries.<\/p>\n<pre>        ImmutableDictionary&lt;string, string&gt; dictionary = ImmutableDictionary\n                                                        .Create&lt;string, string&gt;()\n                                                        .Add(\"key1\", \"value1\")\n                                                        .Add(\"key2\", \"value2\");\n        string value = dictionary.GetValueOrDefault(\"key1\");<\/pre>\n<p>Unfortunately, this results in a compilation error:<\/p>\n<p style=\"padding-left: 30px\"><em>The call is ambiguous between the following methods or properties: &#8216;ImmutableDictionary.GetValueOrDefault&lt;string,string&gt;(IReadOnlyDictionary&lt;string,string&gt;, string)&#8217; and &#8216;ImmutableDictionary.GetValueOrDefault&lt;string,string&gt;(IDictionary&lt;string,string&gt;, string)&#8217;<\/em><\/p>\n<p>The reason is that we offered this extension method for <strong>IDictionary&lt;TKey, TValue&gt;<\/strong> as well as <strong>IReadOnlyDictionary&lt;TKey, TValue&gt;<\/strong>. Since an instance of the concrete<strong> ImmutableDictionary&lt;TKey, TValue&gt;<\/strong> class is both, the compiler cannot decide which one to use. Typing the local variable to either of the types will work. You can also type the local variable as the <strong>IImmutableDictionary&lt;TKey, TValue&gt;<\/strong> interface as the interface only extends<strong> IReadOnlyDictionary&lt;TKey, TValue&gt;<\/strong> (in fact that&rsquo;s why we didn&rsquo;t catch it earlier as this was the code we used in the unit tests).<\/p>\n<p>We&rsquo;ve solved this issue by removing the overloads for <strong>IDictionary&lt;TKey, TValue&gt;<\/strong> and <strong>IReadOnlyDictionary&lt;TKey, TValue&gt;<\/strong>. Instead we added one for<strong> IImmutableDictionary&lt;TKey, TValue&gt;<\/strong>. This solves ambiguity and keeps the immutable package focused on types relating to immutable data structures.<\/p>\n<h2>Summary<\/h2>\n<p>The latest iteration of the immutable collections preview adds an immutable array type. It&rsquo;s a zero overhead wrapper around a regular array that ensures it never changes.<\/p>\n<p>Go play around with it and let us know what you think!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We&rsquo;ve just released an update to our immutable collection package which adds a new member to the family of immutable collection types: ImmutableArray&lt;T&gt;. In this post, I&rsquo;ll talk about why we added another collection and how it relates to the existing types. I&rsquo;ll also cover some minor updates we did to our package. Introduction ImmutableArray&lt;T&gt; [&hellip;]<\/p>\n","protected":false},"author":335,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[43,84,104],"class_list":["post-893","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","tag-bcl","tag-immutable","tag-nuget"],"acf":[],"blog_post_summary":"<p>We&rsquo;ve just released an update to our immutable collection package which adds a new member to the family of immutable collection types: ImmutableArray&lt;T&gt;. In this post, I&rsquo;ll talk about why we added another collection and how it relates to the existing types. I&rsquo;ll also cover some minor updates we did to our package. Introduction ImmutableArray&lt;T&gt; [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/893","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/335"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=893"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/893\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}