Add span-based static Create overloads for immutable collections (#87945)
authorStephen Toub <stoub@microsoft.com>
Fri, 23 Jun 2023 14:43:42 +0000 (10:43 -0400)
committerGitHub <noreply@github.com>
Fri, 23 Jun 2023 14:43:42 +0000 (10:43 -0400)
19 files changed:
src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Builder.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack.cs
src/libraries/System.Collections.Immutable/tests/ImmutableHashSetTest.cs
src/libraries/System.Collections.Immutable/tests/ImmutableListBuilderTest.cs
src/libraries/System.Collections.Immutable/tests/ImmutableListTest.cs
src/libraries/System.Collections.Immutable/tests/ImmutableListTestBase.cs
src/libraries/System.Collections.Immutable/tests/ImmutableQueueTest.cs
src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs
src/libraries/System.Collections.Immutable/tests/ImmutableStackTest.cs

index a9d7404..e643b6a 100644 (file)
@@ -561,8 +561,10 @@ namespace System.Collections.Immutable
         public static System.Collections.Immutable.ImmutableHashSet<T> Create<T>(System.Collections.Generic.IEqualityComparer<T>? equalityComparer) { throw null; }
         public static System.Collections.Immutable.ImmutableHashSet<T> Create<T>(System.Collections.Generic.IEqualityComparer<T>? equalityComparer, T item) { throw null; }
         public static System.Collections.Immutable.ImmutableHashSet<T> Create<T>(System.Collections.Generic.IEqualityComparer<T>? equalityComparer, params T[] items) { throw null; }
+        public static System.Collections.Immutable.ImmutableHashSet<T> Create<T>(System.Collections.Generic.IEqualityComparer<T>? equalityComparer, System.ReadOnlySpan<T> items) { throw null; }
         public static System.Collections.Immutable.ImmutableHashSet<T> Create<T>(T item) { throw null; }
         public static System.Collections.Immutable.ImmutableHashSet<T> Create<T>(params T[] items) { throw null; }
+        public static System.Collections.Immutable.ImmutableHashSet<T> Create<T>(System.ReadOnlySpan<T> items) { throw null; }
         public static System.Collections.Immutable.ImmutableHashSet<TSource> ToImmutableHashSet<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
         public static System.Collections.Immutable.ImmutableHashSet<TSource> ToImmutableHashSet<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Collections.Generic.IEqualityComparer<TSource>? equalityComparer) { throw null; }
         public static System.Collections.Immutable.ImmutableHashSet<TSource> ToImmutableHashSet<TSource>(this System.Collections.Immutable.ImmutableHashSet<TSource>.Builder builder) { throw null; }
@@ -687,6 +689,7 @@ namespace System.Collections.Immutable
         public static System.Collections.Immutable.ImmutableList<T> Create<T>() { throw null; }
         public static System.Collections.Immutable.ImmutableList<T> Create<T>(T item) { throw null; }
         public static System.Collections.Immutable.ImmutableList<T> Create<T>(params T[] items) { throw null; }
+        public static System.Collections.Immutable.ImmutableList<T> Create<T>(System.ReadOnlySpan<T> items) { throw null; }
         public static int IndexOf<T>(this System.Collections.Immutable.IImmutableList<T> list, T item) { throw null; }
         public static int IndexOf<T>(this System.Collections.Immutable.IImmutableList<T> list, T item, System.Collections.Generic.IEqualityComparer<T>? equalityComparer) { throw null; }
         public static int IndexOf<T>(this System.Collections.Immutable.IImmutableList<T> list, T item, int startIndex) { throw null; }
@@ -881,6 +884,7 @@ namespace System.Collections.Immutable
         public static System.Collections.Immutable.ImmutableQueue<T> Create<T>() { throw null; }
         public static System.Collections.Immutable.ImmutableQueue<T> Create<T>(T item) { throw null; }
         public static System.Collections.Immutable.ImmutableQueue<T> Create<T>(params T[] items) { throw null; }
+        public static System.Collections.Immutable.ImmutableQueue<T> Create<T>(System.ReadOnlySpan<T> items) { throw null; }
         public static System.Collections.Immutable.IImmutableQueue<T> Dequeue<T>(this System.Collections.Immutable.IImmutableQueue<T> queue, out T value) { throw null; }
     }
     public sealed partial class ImmutableQueue<T> : System.Collections.Generic.IEnumerable<T>, System.Collections.IEnumerable, System.Collections.Immutable.IImmutableQueue<T>
@@ -1055,8 +1059,10 @@ namespace System.Collections.Immutable
         public static System.Collections.Immutable.ImmutableSortedSet<T> Create<T>(System.Collections.Generic.IComparer<T>? comparer) { throw null; }
         public static System.Collections.Immutable.ImmutableSortedSet<T> Create<T>(System.Collections.Generic.IComparer<T>? comparer, T item) { throw null; }
         public static System.Collections.Immutable.ImmutableSortedSet<T> Create<T>(System.Collections.Generic.IComparer<T>? comparer, params T[] items) { throw null; }
+        public static System.Collections.Immutable.ImmutableSortedSet<T> Create<T>(System.Collections.Generic.IComparer<T>? comparer, System.ReadOnlySpan<T> items) { throw null; }
         public static System.Collections.Immutable.ImmutableSortedSet<T> Create<T>(T item) { throw null; }
         public static System.Collections.Immutable.ImmutableSortedSet<T> Create<T>(params T[] items) { throw null; }
+        public static System.Collections.Immutable.ImmutableSortedSet<T> Create<T>(System.ReadOnlySpan<T> items) { throw null; }
         public static System.Collections.Immutable.ImmutableSortedSet<TSource> ToImmutableSortedSet<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
         public static System.Collections.Immutable.ImmutableSortedSet<TSource> ToImmutableSortedSet<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }
         public static System.Collections.Immutable.ImmutableSortedSet<TSource> ToImmutableSortedSet<TSource>(this System.Collections.Immutable.ImmutableSortedSet<TSource>.Builder builder) { throw null; }
@@ -1187,6 +1193,7 @@ namespace System.Collections.Immutable
         public static System.Collections.Immutable.ImmutableStack<T> Create<T>() { throw null; }
         public static System.Collections.Immutable.ImmutableStack<T> Create<T>(T item) { throw null; }
         public static System.Collections.Immutable.ImmutableStack<T> Create<T>(params T[] items) { throw null; }
+        public static System.Collections.Immutable.ImmutableStack<T> Create<T>(System.ReadOnlySpan<T> items) { throw null; }
         public static System.Collections.Immutable.IImmutableStack<T> Pop<T>(this System.Collections.Immutable.IImmutableStack<T> stack, out T value) { throw null; }
     }
     public sealed partial class ImmutableStack<T> : System.Collections.Generic.IEnumerable<T>, System.Collections.IEnumerable, System.Collections.Immutable.IImmutableStack<T>
index 6c65501..9630bb3 100644 (file)
@@ -87,6 +87,19 @@ namespace System.Collections.Immutable
         /// <returns>The new immutable collection.</returns>
         public static ImmutableHashSet<T> Create<T>(params T[] items)
         {
+            Requires.NotNull(items, nameof(items));
+
+            return Create((ReadOnlySpan<T>)items);
+        }
+
+        /// <summary>
+        /// Creates a new immutable collection prefilled with the specified items.
+        /// </summary>
+        /// <typeparam name="T">The type of items stored by the collection.</typeparam>
+        /// <param name="items">The items to prepopulate.</param>
+        /// <returns>The new immutable collection.</returns>
+        public static ImmutableHashSet<T> Create<T>(ReadOnlySpan<T> items)
+        {
             return ImmutableHashSet<T>.Empty.Union(items);
         }
 
@@ -99,6 +112,20 @@ namespace System.Collections.Immutable
         /// <returns>The new immutable collection.</returns>
         public static ImmutableHashSet<T> Create<T>(IEqualityComparer<T>? equalityComparer, params T[] items)
         {
+            Requires.NotNull(items, nameof(items));
+
+            return Create(equalityComparer, (ReadOnlySpan<T>)items);
+        }
+
+        /// <summary>
+        /// Creates a new immutable collection prefilled with the specified items.
+        /// </summary>
+        /// <typeparam name="T">The type of items stored by the collection.</typeparam>
+        /// <param name="equalityComparer">The equality comparer.</param>
+        /// <param name="items">The items to prepopulate.</param>
+        /// <returns>The new immutable collection.</returns>
+        public static ImmutableHashSet<T> Create<T>(IEqualityComparer<T>? equalityComparer, ReadOnlySpan<T> items)
+        {
             return ImmutableHashSet<T>.Empty.WithComparer(equalityComparer).Union(items);
         }
 
index 0a0dce7..b0117e8 100644 (file)
@@ -3,7 +3,6 @@
 
 using System.Collections.Generic;
 using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 
 namespace System.Collections.Immutable
@@ -242,6 +241,14 @@ namespace System.Collections.Immutable
         /// <summary>
         /// See the <see cref="IImmutableSet{T}"/> interface.
         /// </summary>
+        internal ImmutableHashSet<T> Union(ReadOnlySpan<T> other)
+        {
+            return Union(other, this.Origin).Finalize(this);
+        }
+
+        /// <summary>
+        /// See the <see cref="IImmutableSet{T}"/> interface.
+        /// </summary>
         public ImmutableHashSet<T> Intersect(IEnumerable<T> other)
         {
             Requires.NotNull(other, nameof(other));
@@ -696,6 +703,29 @@ namespace System.Collections.Immutable
         /// <summary>
         /// Performs the set operation on a given data structure.
         /// </summary>
+        private static MutationResult Union(ReadOnlySpan<T> other, MutationInput origin)
+        {
+            int count = 0;
+            SortedInt32KeyNode<ImmutableHashSet<T>.HashBucket> newRoot = origin.Root;
+            foreach (T item in other)
+            {
+                int hashCode = item != null ? origin.EqualityComparer.GetHashCode(item) : 0;
+                HashBucket bucket = newRoot.GetValueOrDefault(hashCode);
+                OperationResult result;
+                ImmutableHashSet<T>.HashBucket newBucket = bucket.Add(item, origin.EqualityComparer, out result);
+                if (result == OperationResult.SizeChanged)
+                {
+                    newRoot = UpdateRoot(newRoot, hashCode, origin.HashBucketEqualityComparer, newBucket);
+                    count++;
+                }
+            }
+
+            return new MutationResult(newRoot, count);
+        }
+
+        /// <summary>
+        /// Performs the set operation on a given data structure.
+        /// </summary>
         private static bool Overlaps(IEnumerable<T> other, MutationInput origin)
         {
             Requires.NotNull(other, nameof(other));
index 9ed4ab5..f3004e3 100644 (file)
@@ -36,10 +36,23 @@ namespace System.Collections.Immutable
         /// <summary>
         /// Creates a new immutable collection prefilled with the specified items.
         /// </summary>
-        /// <typeparam name="T">The type of items stored by the collection.</typeparam>
-        /// <param name="items">The items to prepopulate.</param>
+        /// <typeparam name="T">The type of items in the immutable list.</typeparam>
+        /// <param name="items">A span that contains the items to prepopulate the list with.</param>
         /// <returns>The new immutable collection.</returns>
-        public static ImmutableList<T> Create<T>(params T[] items) => ImmutableList<T>.Empty.AddRange(items);
+        public static ImmutableList<T> Create<T>(params T[] items)
+        {
+            Requires.NotNull(items, nameof(items));
+
+            return Create((ReadOnlySpan<T>)items);
+        }
+
+        /// <summary>
+        /// Creates a new immutable list that contains the items from the specified span of items.
+        /// </summary>
+        /// <typeparam name="T">The type of items stored by the collection.</typeparam>
+        /// <param name="items">A span that contains the items to prepopulate the list with.</param>
+        /// <returns>A new immutable list that contains the specified items.</returns>
+        public static ImmutableList<T> Create<T>(ReadOnlySpan<T> items) => ImmutableList<T>.Empty.AddRange(items);
 
         /// <summary>
         /// Creates a new immutable list builder.
index 620a956..803e131 100644 (file)
@@ -18,7 +18,7 @@ namespace System.Collections.Immutable
         /// </summary>
         /// <remarks>
         /// <para>
-        /// While <see cref="ImmutableList{T}.AddRange"/> and other bulk change methods
+        /// While <see cref="M:ImmutableList{T}.AddRange"/> and other bulk change methods
         /// already provide fast bulk change operations on the collection, this class allows
         /// multiple combinations of changes to be made to a set with equal efficiency.
         /// </para>
index 1eeec15..36e3441 100644 (file)
@@ -279,6 +279,25 @@ namespace System.Collections.Immutable
             }
 
             /// <summary>
+            /// Creates a node tree that contains the contents of a span.
+            /// </summary>
+            /// <param name="items">A span with the contents that the new node tree should contain.</param>
+            /// <returns>The root of the created node tree.</returns>
+            internal static Node NodeTreeFromList(ReadOnlySpan<T> items)
+            {
+                if (items.IsEmpty)
+                {
+                    return EmptyNode;
+                }
+
+                int rightCount = (items.Length - 1) / 2;
+                int leftCount = (items.Length - 1) - rightCount;
+                Node left = NodeTreeFromList(items.Slice(0, leftCount));
+                Node right = NodeTreeFromList(items.Slice(leftCount + 1));
+                return new Node(items[leftCount], left, right, frozen: true);
+            }
+
+            /// <summary>
             /// Adds the specified key to the tree.
             /// </summary>
             /// <param name="key">The key.</param>
@@ -344,6 +363,23 @@ namespace System.Collections.Immutable
             }
 
             /// <summary>
+            /// Adds the specified keys to this tree.
+            /// </summary>
+            /// <param name="keys">The keys.</param>
+            /// <returns>The new tree.</returns>
+            internal Node AddRange(ReadOnlySpan<T> keys)
+            {
+                if (this.IsEmpty)
+                {
+                    return NodeTreeFromList(keys);
+                }
+
+                Node newRight = _right!.AddRange(keys);
+                Node result = this.MutateRight(newRight);
+                return result.BalanceMany();
+            }
+
+            /// <summary>
             /// Adds the specified keys at a given index to this tree.
             /// </summary>
             /// <param name="index">The location for the new keys.</param>
index 9d0d342..966244f 100644 (file)
@@ -236,6 +236,31 @@ namespace System.Collections.Immutable
         /// <summary>
         /// See the <see cref="IImmutableList{T}"/> interface.
         /// </summary>
+        internal ImmutableList<T> AddRange(ReadOnlySpan<T> items)
+        {
+            if (this.IsEmpty)
+            {
+                if (items.IsEmpty)
+                {
+                    return Empty;
+                }
+
+                return new ImmutableList<T>(Node.NodeTreeFromList(items));
+            }
+            else
+            {
+                if (items.IsEmpty)
+                {
+                    return this;
+                }
+
+                return this.Wrap(_root.AddRange(items));
+            }
+        }
+
+        /// <summary>
+        /// See the <see cref="IImmutableList{T}"/> interface.
+        /// </summary>
         public ImmutableList<T> Insert(int index, T item)
         {
             Requires.Range(index >= 0 && index <= this.Count, nameof(index));
index f407923..122bae1 100644 (file)
@@ -74,7 +74,18 @@ namespace System.Collections.Immutable
         {
             Requires.NotNull(items, nameof(items));
 
-            if (items.Length == 0)
+            return Create((ReadOnlySpan<T>)items);
+        }
+
+        /// <summary>
+        /// Creates a new immutable queue that contains the specified array of items.
+        /// </summary>
+        /// <typeparam name="T">The type of items in the immutable queue.</typeparam>
+        /// <param name="items">A span that contains the items to prepopulate the queue with.</param>
+        /// <returns>A new immutable queue that contains the specified items.</returns>
+        public static ImmutableQueue<T> Create<T>(ReadOnlySpan<T> items)
+        {
+            if (items.IsEmpty)
             {
                 return ImmutableQueue<T>.Empty;
             }
index 54548ff..567997e 100644 (file)
@@ -87,6 +87,18 @@ namespace System.Collections.Immutable
         /// <returns>The new immutable collection.</returns>
         public static ImmutableSortedSet<T> Create<T>(params T[] items)
         {
+            Requires.NotNull(items, nameof(items));
+            return Create((ReadOnlySpan<T>)items);
+        }
+
+        /// <summary>
+        /// Creates a new immutable sorted set that contains the specified array of items.
+        /// </summary>
+        /// <typeparam name="T">The type of items in the immutable set.</typeparam>
+        /// <param name="items">A span that contains the items to prepopulate the set with.</param>
+        /// <returns>A new immutable set that contains the specified items.</returns>
+        public static ImmutableSortedSet<T> Create<T>(ReadOnlySpan<T> items)
+        {
             return ImmutableSortedSet<T>.Empty.Union(items);
         }
 
@@ -99,6 +111,20 @@ namespace System.Collections.Immutable
         /// <returns>The new immutable collection.</returns>
         public static ImmutableSortedSet<T> Create<T>(IComparer<T>? comparer, params T[] items)
         {
+            Requires.NotNull(items, nameof(items));
+
+            return Create(comparer, (ReadOnlySpan<T>)items);
+        }
+
+        /// <summary>
+        /// Creates a new immutable collection prefilled with the specified items.
+        /// </summary>
+        /// <typeparam name="T">The type of items stored by the collection.</typeparam>
+        /// <param name="comparer">The comparer.</param>
+        /// <param name="items">The items to prepopulate.</param>
+        /// <returns>The new immutable collection.</returns>
+        public static ImmutableSortedSet<T> Create<T>(IComparer<T>? comparer, ReadOnlySpan<T> items)
+        {
             return ImmutableSortedSet<T>.Empty.WithComparer(comparer).Union(items);
         }
 
index e202ccd..1aa9449 100644 (file)
@@ -18,7 +18,7 @@ namespace System.Collections.Immutable
         /// </summary>
         /// <remarks>
         /// <para>
-        /// While <see cref="ImmutableSortedSet{T}.Union"/> and other bulk change methods
+        /// While <see cref="M:ImmutableSortedSet{T}.Union"/> and other bulk change methods
         /// already provide fast bulk change operations on the collection, this class allows
         /// multiple combinations of changes to be made to a set with equal efficiency.
         /// </para>
index ba83287..493172c 100644 (file)
@@ -337,6 +337,23 @@ namespace System.Collections.Immutable
         /// <summary>
         /// See the <see cref="IImmutableSet{T}"/> interface.
         /// </summary>
+        internal ImmutableSortedSet<T> Union(ReadOnlySpan<T> other)
+        {
+            if (this.IsEmpty || (this.Count + other.Length) * RefillOverIncrementalThreshold > this.Count)
+            {
+                // The payload being added is so large compared to this collection's current size
+                // that we likely won't see much memory reuse in the node tree by performing an
+                // incremental update.  So just recreate the entire node tree since that will
+                // likely be faster.
+                return this.LeafToRootRefill(other);
+            }
+
+            return this.UnionIncremental(other);
+        }
+
+        /// <summary>
+        /// See the <see cref="IImmutableSet{T}"/> interface.
+        /// </summary>
         public ImmutableSortedSet<T> WithComparer(IComparer<T>? comparer)
         {
             comparer ??= Comparer<T>.Default;
@@ -1042,8 +1059,31 @@ namespace System.Collections.Immutable
             ImmutableSortedSet<T>.Node result = _root;
             foreach (T item in items.GetEnumerableDisposable<T, Enumerator>())
             {
-                bool mutated;
-                result = result.Add(item, _comparer, out mutated);
+                result = result.Add(item, _comparer, out _);
+            }
+
+            return this.Wrap(result);
+        }
+
+        /// <summary>
+        /// Adds items to this collection using the standard spine rewrite and tree rebalance technique.
+        /// </summary>
+        /// <param name="items">The items to add.</param>
+        /// <returns>The new collection.</returns>
+        /// <remarks>
+        /// This method is least demanding on memory, providing the great chance of memory reuse
+        /// and does not require allocating memory large enough to store all items contiguously.
+        /// It's performance is optimal for additions that do not significantly dwarf the existing
+        /// size of this collection.
+        /// </remarks>
+        private ImmutableSortedSet<T> UnionIncremental(ReadOnlySpan<T> items)
+        {
+            // Let's not implement in terms of ImmutableSortedSet.Add so that we're
+            // not unnecessarily generating a new wrapping set object for each item.
+            ImmutableSortedSet<T>.Node result = _root;
+            foreach (T item in items)
+            {
+                result = result.Add(item, _comparer, out _);
             }
 
             return this.Wrap(result);
@@ -1132,6 +1172,64 @@ namespace System.Collections.Immutable
         }
 
         /// <summary>
+        /// Creates an immutable sorted set with the contents from this collection and a sequence of elements.
+        /// </summary>
+        /// <param name="addedItems">The sequence of elements to add to this set.</param>
+        /// <returns>The immutable sorted set.</returns>
+        private ImmutableSortedSet<T> LeafToRootRefill(ReadOnlySpan<T> addedItems)
+        {
+            // See comments in LeafToRootRefill(IEnumerable<T> addedItems)
+
+            // Produce the initial list containing all elements, including any duplicates.
+            List<T> list;
+            if (this.IsEmpty && addedItems.IsEmpty)
+            {
+                // If the additional items enumerable list is known to be empty, too,
+                // then just return this empty instance.
+                if (addedItems.IsEmpty)
+                {
+                    return this;
+                }
+
+                list = new List<T>(addedItems.Length);
+            }
+            else
+            {
+                // Build the list from this set and then add the additional items.
+                // Even if the additional items is empty, this set isn't, so we know
+                // the resulting list will not be empty.
+                list = new List<T>(this.Count + addedItems.Length);
+                list.AddRange(this);
+            }
+#if NET8_0_OR_GREATER
+            list.AddRange(addedItems);
+#else
+            foreach (var item in addedItems)
+            {
+                list.Add(item);
+            }
+#endif
+            Debug.Assert(list.Count > 0);
+
+            // Sort the list and remove duplicate entries.
+            IComparer<T> comparer = this.KeyComparer;
+            list.Sort(comparer);
+            int index = 1;
+            for (int i = 1; i < list.Count; i++)
+            {
+                if (comparer.Compare(list[i], list[i - 1]) != 0)
+                {
+                    list[index++] = list[i];
+                }
+            }
+            list.RemoveRange(index, list.Count - index);
+
+            // Use the now sorted list of unique items to construct a new sorted set.
+            Node root = Node.NodeTreeFromList(list.AsOrderedCollection(), 0, list.Count);
+            return this.Wrap(root);
+        }
+
+        /// <summary>
         /// An reverse enumerable of a sorted set.
         /// </summary>
         private sealed class ReverseEnumerable : IEnumerable<T>
index 9acbdf2..4e11bb7 100644 (file)
@@ -61,6 +61,17 @@ namespace System.Collections.Immutable
         {
             Requires.NotNull(items, nameof(items));
 
+            return Create((ReadOnlySpan<T>)items);
+        }
+
+        /// <summary>
+        /// Creates a new immutable stack that contains the specified array of items.
+        /// </summary>
+        /// <typeparam name="T">The type of items in the immutable stack.</typeparam>
+        /// <param name="items">A span that contains the items to prepopulate the stack with.</param>
+        /// <returns>A new immutable stack that contains the specified items.</returns>
+        public static ImmutableStack<T> Create<T>(ReadOnlySpan<T> items)
+        {
             ImmutableStack<T> stack = ImmutableStack<T>.Empty;
             foreach (T item in items)
             {
index 5c31691..0aab940 100644 (file)
@@ -122,10 +122,18 @@ namespace System.Collections.Immutable.Tests
             Assert.Equal(2, set.Count);
             Assert.Same(EqualityComparer<string>.Default, set.KeyComparer);
 
+            set = ImmutableHashSet.Create((ReadOnlySpan<string>)new[] { "a", "b" });
+            Assert.Equal(2, set.Count);
+            Assert.Same(EqualityComparer<string>.Default, set.KeyComparer);
+
             set = ImmutableHashSet.Create(comparer, "a", "b");
             Assert.Equal(2, set.Count);
             Assert.Same(comparer, set.KeyComparer);
 
+            set = ImmutableHashSet.Create(comparer, (ReadOnlySpan<string>)new[] { "a", "b" });
+            Assert.Equal(2, set.Count);
+            Assert.Same(comparer, set.KeyComparer);
+
             set = ImmutableHashSet.CreateRange((IEnumerable<string>)new[] { "a", "b" });
             Assert.Equal(2, set.Count);
             Assert.Same(EqualityComparer<string>.Default, set.KeyComparer);
index f6e60f0..897a00b 100644 (file)
@@ -476,7 +476,7 @@ namespace System.Collections.Immutable.Tests
 
         protected override IEnumerable<T> GetEnumerableOf<T>(params T[] contents)
         {
-            return ImmutableList<T>.Empty.AddRange(contents).ToBuilder();
+            return ImmutableList<T>.Empty.AddRange((ReadOnlySpan<T>)contents).ToBuilder();
         }
 
         protected override void RemoveAllTestHelper<T>(ImmutableList<T> list, Predicate<T> test)
index 2756034..707a66f 100644 (file)
@@ -51,7 +51,7 @@ namespace System.Collections.Immutable.Tests
                         int[] values = Enumerable.Range(0, inputLength).Select(i => random.Next()).ToArray();
                         Debug.WriteLine("Adding {0} elements to the list.", inputLength);
                         expected.AddRange(values);
-                        actual = actual.AddRange(values);
+                        actual = actual.AddRange((IEnumerable<int>)values);
                         VerifyBalanced(actual);
                         break;
                     case Operation.Insert:
@@ -139,10 +139,10 @@ namespace System.Collections.Immutable.Tests
         public void AddRangeTest()
         {
             ImmutableList<int> list = ImmutableList<int>.Empty;
-            list = list.AddRange(new[] { 1, 2, 3 });
+            list = list.AddRange((IEnumerable<int>)new[] { 1, 2, 3 });
             list = list.AddRange(Enumerable.Range(4, 2));
-            list = list.AddRange(ImmutableList<int>.Empty.AddRange(new[] { 6, 7, 8 }));
-            list = list.AddRange(new int[0]);
+            list = list.AddRange(ImmutableList<int>.Empty.AddRange((IEnumerable<int>)new[] { 6, 7, 8 }));
+            list = list.AddRange((IEnumerable<int>)new int[0]);
             list = list.AddRange(ImmutableList<int>.Empty.AddRange(Enumerable.Range(9, 1000)));
             Assert.Equal(Enumerable.Range(1, 1008), list);
         }
@@ -165,7 +165,7 @@ namespace System.Collections.Immutable.Tests
             ImmutableList<string> emptyList = ImmutableList.Create<string>();
 
             // Adding an empty list to an empty list should yield the original list.
-            Assert.Same(emptyList, emptyList.AddRange(new string[0]));
+            Assert.Same(emptyList, emptyList.AddRange(Enumerable.Empty<string>()));
 
             // Adding a non-empty immutable list to an empty one should return the added list.
             ImmutableList<string> nonEmptyListDefaultComparer = ImmutableList.Create("5");
@@ -587,6 +587,9 @@ namespace System.Collections.Immutable.Tests
             list = ImmutableList.Create("a", "b");
             Assert.Equal(2, list.Count);
 
+            list = ImmutableList.Create((ReadOnlySpan<string>)new[] { "a", "b" });
+            Assert.Equal(2, list.Count);
+
             list = ImmutableList.CreateRange((IEnumerable<string>)new[] { "a", "b" });
             Assert.Equal(2, list.Count);
         }
@@ -836,7 +839,7 @@ namespace System.Collections.Immutable.Tests
 
         protected override IEnumerable<T> GetEnumerableOf<T>(params T[] contents)
         {
-            return ImmutableList<T>.Empty.AddRange(contents);
+            return ImmutableList<T>.Empty.AddRange((IEnumerable<T>)contents);
         }
 
         protected override void RemoveAllTestHelper<T>(ImmutableList<T> list, Predicate<T> test)
index 1144529..09eeac7 100644 (file)
@@ -91,7 +91,7 @@ namespace System.Collections.Immutable.Tests
         public void FindAllTest()
         {
             Assert.True(this.GetListQuery(ImmutableList<int>.Empty).FindAll(n => true).IsEmpty);
-            ImmutableList<int> list = ImmutableList<int>.Empty.AddRange(new[] { 2, 3, 4, 5, 6 });
+            ImmutableList<int> list = ImmutableList<int>.Empty.AddRange((IEnumerable<int>)new[] { 2, 3, 4, 5, 6 });
             ImmutableList<int> actual = this.GetListQuery(list).FindAll(n => n % 2 == 1);
             List<int> expected = list.ToList().FindAll(n => n % 2 == 1);
             Assert.Equal<int>(expected, actual.ToList());
@@ -101,7 +101,7 @@ namespace System.Collections.Immutable.Tests
         public void FindTest()
         {
             Assert.Equal(0, this.GetListQuery(ImmutableList<int>.Empty).Find(n => true));
-            ImmutableList<int> list = ImmutableList<int>.Empty.AddRange(new[] { 2, 3, 4, 5, 6 });
+            ImmutableList<int> list = ImmutableList<int>.Empty.AddRange((IEnumerable<int>)new[] { 2, 3, 4, 5, 6 });
             Assert.Equal(3, this.GetListQuery(list).Find(n => (n % 2) == 1));
         }
 
@@ -109,7 +109,7 @@ namespace System.Collections.Immutable.Tests
         public void FindLastTest()
         {
             Assert.Equal(0, this.GetListQuery(ImmutableList<int>.Empty).FindLast(n => { throw new ShouldNotBeInvokedException(); }));
-            ImmutableList<int> list = ImmutableList<int>.Empty.AddRange(new[] { 2, 3, 4, 5, 6 });
+            ImmutableList<int> list = ImmutableList<int>.Empty.AddRange((IEnumerable<int>)new[] { 2, 3, 4, 5, 6 });
             Assert.Equal(5, this.GetListQuery(list).FindLast(n => (n % 2) == 1));
         }
 
index dc0de12..3652401 100644 (file)
@@ -217,6 +217,10 @@ namespace System.Collections.Immutable.Tests
             Assert.False(queue.IsEmpty);
             Assert.Equal(new[] { 1, 2 }, queue);
 
+            queue = ImmutableQueue.Create((ReadOnlySpan<int>)new[] { 1, 2 });
+            Assert.False(queue.IsEmpty);
+            Assert.Equal(new[] { 1, 2 }, queue);
+
             queue = ImmutableQueue.CreateRange((IEnumerable<int>)new[] { 1, 2 });
             Assert.False(queue.IsEmpty);
             Assert.Equal(new[] { 1, 2 }, queue);
index 6b90b2a..1f6808c 100644 (file)
@@ -49,7 +49,7 @@ namespace System.Collections.Immutable.Tests
                         break;
                     case Operation.Union:
                         int inputLength = random.Next(100);
-                        int[] values = Enumerable.Range(0, inputLength).Select(i => random.Next()).ToArray();
+                        IEnumerable<int> values = Enumerable.Range(0, inputLength).Select(i => random.Next()).ToArray();
                         Debug.WriteLine("Adding {0} elements to the set.", inputLength);
                         expected.UnionWith(values);
                         actual = actual.Union(values);
@@ -293,10 +293,18 @@ namespace System.Collections.Immutable.Tests
             Assert.Equal(2, set.Count);
             Assert.Same(Comparer<string>.Default, set.KeyComparer);
 
+            set = ImmutableSortedSet.Create((ReadOnlySpan<string>)new[] { "a", "b" });
+            Assert.Equal(2, set.Count);
+            Assert.Same(Comparer<string>.Default, set.KeyComparer);
+
             set = ImmutableSortedSet.Create(comparer, "a", "b");
             Assert.Equal(2, set.Count);
             Assert.Same(comparer, set.KeyComparer);
 
+            set = ImmutableSortedSet.Create(comparer, (ReadOnlySpan<string>)new[] { "a", "b" });
+            Assert.Equal(2, set.Count);
+            Assert.Same(comparer, set.KeyComparer);
+
             set = ImmutableSortedSet.CreateRange((IEnumerable<string>)new[] { "a", "b" });
             Assert.Equal(2, set.Count);
             Assert.Same(Comparer<string>.Default, set.KeyComparer);
index 60c3922..1813326 100644 (file)
@@ -245,6 +245,10 @@ namespace System.Collections.Immutable.Tests
             Assert.False(stack.IsEmpty);
             Assert.Equal(new[] { 2, 1 }, stack);
 
+            stack = ImmutableStack.Create((ReadOnlySpan<int>)new[] { 1, 2 });
+            Assert.False(stack.IsEmpty);
+            Assert.Equal(new[] { 2, 1 }, stack);
+
             stack = ImmutableStack.CreateRange((IEnumerable<int>)new[] { 1, 2 });
             Assert.False(stack.IsEmpty);
             Assert.Equal(new[] { 2, 1 }, stack);