diff --git a/src/Common/src/System/Collections/Generic/EnumerableHelpers.cs b/src/Common/src/System/Collections/Generic/EnumerableHelpers.cs
new file mode 100644
index 000000000000..6cd46fd32efd
--- /dev/null
+++ b/src/Common/src/System/Collections/Generic/EnumerableHelpers.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace System.Collections.Generic
+{
+ /// Internal helper functions for working with enumerables.
+ internal static class EnumerableHelpers
+ {
+ /// Converts an enumerable to an array using the same logic as does List{T}.
+ /// The number of items stored in the resulting array, 0-indexed.
+ ///
+ /// The resulting array. The length of the array may be greater than ,
+ /// which is the actual number of elements in the array.
+ ///
+ internal static T[] ToArray(IEnumerable source, out int length)
+ {
+ T[] arr;
+ int count = 0;
+
+ ICollection ic = source as ICollection;
+ if (ic != null)
+ {
+ count = ic.Count;
+ if (count == 0)
+ {
+ arr = Array.Empty();
+ }
+ else
+ {
+ // Allocate an array of the desired size, then copy the elements into it. Note that this has the same
+ // issue regarding concurrency as other existing collections like List. If the collection size
+ // concurrently changes between the array allocation and the CopyTo, we could end up either getting an
+ // exception from overrunning the array (if the size went up) or we could end up not filling as many
+ // items as 'count' suggests (if the size went down). This is only an issue for concurrent collections
+ // that implement ICollection, which as of .NET 4.6 is just ConcurrentDictionary.
+ arr = new T[count];
+ ic.CopyTo(arr, 0);
+ }
+ }
+ else
+ {
+ arr = Array.Empty();
+ foreach (var item in source)
+ {
+ if (count == arr.Length)
+ {
+ // MaxArrayLength is defined in Array.MaxArrayLength and in gchelpers in CoreCLR.
+ // It represents the maximum number of elements that can be in an array where
+ // the size of the element is greater than one byte; a separate, slightly larger constant,
+ // is used when the size of the element is one.
+ const int MaxArrayLength = 0x7FEFFFFF;
+
+ // This is the same growth logic as in List:
+ // If the array is currently empty, we make it a default size. Otherwise, we attempt to
+ // double the size of the array. Doubling will overflow once the size of the array reaches
+ // 2^30, since doubling to 2^31 is 1 larger than Int32.MaxValue. In that case, we instead
+ // constrain the length to be MaxArrayLength (this overflow check works because of of the
+ // cast to uint). Because a slightly larger constant is used when T is one byte in size, we
+ // could then end up in a situation where arr.Length is MaxArrayLength or slightly larger, such
+ // that we constrain newLength to be MaxArrayLength but the needed number of elements is actually
+ // larger than that. For that case, we then ensure that the newLength is large enough to hold
+ // the desired capacity. This does mean that in the very rare case where we've grown to such a
+ // large size, each new element added after MaxArrayLength will end up doing a resize.
+ const int DefaultCapacity = 4;
+ int newLength = count == 0 ? DefaultCapacity : count * 2;
+ if ((uint)newLength > MaxArrayLength)
+ {
+ newLength = MaxArrayLength;
+ }
+ if (newLength < count + 1)
+ {
+ newLength = count + 1;
+ }
+
+ arr = ArrayT.Resize(arr, newLength, count);
+ }
+ arr[count++] = item;
+ }
+ }
+
+ length = count;
+ return arr;
+ }
+ }
+}
diff --git a/src/System.Collections/src/System.Collections.csproj b/src/System.Collections/src/System.Collections.csproj
index b3eff2b74d43..d0d7de672979 100644
--- a/src/System.Collections/src/System.Collections.csproj
+++ b/src/System.Collections/src/System.Collections.csproj
@@ -42,11 +42,14 @@
+
+ Common\System\ArrayT.cs
+
Common\System\Collections\HashHelpers.cs
-
- Common\System\ArrayT.cs
+
+ Common\System\Collections\Generic\EnumerableHelpers.cs
diff --git a/src/System.Collections/src/System/Collections/Generic/Queue.cs b/src/System.Collections/src/System/Collections/Generic/Queue.cs
index 3677c0a94a4f..8b8fb66fe76b 100644
--- a/src/System.Collections/src/System/Collections/Generic/Queue.cs
+++ b/src/System.Collections/src/System/Collections/Generic/Queue.cs
@@ -32,14 +32,13 @@ public class Queue : IEnumerable,
private const int MinimumGrow = 4;
private const int GrowFactor = 200; // double each time
private const int DefaultCapacity = 4;
- private static T[] s_emptyArray = Array.Empty();
// Creates a queue with room for capacity objects. The default initial
// capacity and grow factor are used.
///
public Queue()
{
- _array = s_emptyArray;
+ _array = Array.Empty();
}
// Creates a queue with room for capacity objects. The default grow factor
@@ -50,11 +49,7 @@ public Queue(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException("capacity", SR.ArgumentOutOfRange_NeedNonNegNumRequired);
-
_array = new T[capacity];
- _head = 0;
- _tail = 0;
- _size = 0;
}
// Fills a Queue with the elements of an ICollection. Uses the enumerator
@@ -67,8 +62,6 @@ public Queue(IEnumerable collection)
throw new ArgumentNullException("collection");
_array = new T[DefaultCapacity];
- _size = 0;
- _version = 0;
using (IEnumerator en = collection.GetEnumerator())
{
diff --git a/src/System.Collections/src/System/Collections/Generic/SortedSet.cs b/src/System.Collections/src/System/Collections/Generic/SortedSet.cs
index 3e4c7485ab23..bca8f9c5f6c0 100644
--- a/src/System.Collections/src/System/Collections/Generic/SortedSet.cs
+++ b/src/System.Collections/src/System/Collections/Generic/SortedSet.cs
@@ -95,13 +95,9 @@ public SortedSet(IEnumerable collection, IComparer comparer)
//breadth first traversal to recreate nodes
if (baseSortedSet.Count == 0)
{
- _count = 0;
- _version = 0;
- _root = null;
return;
}
-
//pre order way to replicate nodes
Stack theirStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2);
Stack myStack = new Stack.Node>(2 * log2(baseSortedSet.Count) + 2);
@@ -138,23 +134,27 @@ public SortedSet(IEnumerable collection, IComparer comparer)
}
}
_count = baseSortedSet._count;
- _version = 0;
}
else
- { //As it stands, you're doing an NlogN sort of the collection
- List els = new List(collection);
- els.Sort(_comparer);
- for (int i = 1; i < els.Count; i++)
+ {
+ int count;
+ T[] els = EnumerableHelpers.ToArray(collection, out count);
+ if (count > 0)
{
- if (comparer.Compare(els[i], els[i - 1]) == 0)
+ Array.Sort(els, 0, count, _comparer);
+ int index = 1;
+ for (int i = 1; i < count; i++)
{
- els.RemoveAt(i);
- i--;
+ if (comparer.Compare(els[i], els[i - 1]) != 0)
+ {
+ els[index++] = els[i];
+ }
}
+ count = index;
+
+ _root = ConstructRootFromSortedArray(els, 0, count - 1, null);
+ _count = count;
}
- _root = ConstructRootFromSortedArray(els.ToArray(), 0, els.Count - 1, null);
- _count = els.Count;
- _version = 0;
}
}
@@ -257,25 +257,24 @@ internal virtual bool BreadthFirstTreeWalk(TreeWalkPredicate action)
return true;
}
- List processQueue = new List();
- processQueue.Add(_root);
+ Queue processQueue = new Queue();
+ processQueue.Enqueue(_root);
Node current;
while (processQueue.Count != 0)
{
- current = processQueue[0];
- processQueue.RemoveAt(0);
+ current = processQueue.Dequeue();
if (!action(current))
{
return false;
}
if (current.Left != null)
{
- processQueue.Add(current.Left);
+ processQueue.Enqueue(current.Left);
}
if (current.Right != null)
{
- processQueue.Add(current.Right);
+ processQueue.Enqueue(current.Right);
}
}
return true;
@@ -2084,25 +2083,24 @@ internal override bool BreadthFirstTreeWalk(TreeWalkPredicate action)
return true;
}
- List processQueue = new List();
- processQueue.Add(_root);
+ Queue processQueue = new Queue();
+ processQueue.Enqueue(_root);
Node current;
while (processQueue.Count != 0)
{
- current = processQueue[0];
- processQueue.RemoveAt(0);
+ current = processQueue.Dequeue();
if (IsWithinRange(current.Item) && !action(current))
{
return false;
}
if (current.Left != null && (!_lBoundActive || Comparer.Compare(_min, current.Item) < 0))
{
- processQueue.Add(current.Left);
+ processQueue.Enqueue(current.Left);
}
if (current.Right != null && (!_uBoundActive || Comparer.Compare(_max, current.Item) > 0))
{
- processQueue.Add(current.Right);
+ processQueue.Enqueue(current.Right);
}
}
return true;
diff --git a/src/System.Collections/src/System/Collections/Generic/Stack.cs b/src/System.Collections/src/System/Collections/Generic/Stack.cs
index 9e607a6fbb04..7d3731647704 100644
--- a/src/System.Collections/src/System/Collections/Generic/Stack.cs
+++ b/src/System.Collections/src/System/Collections/Generic/Stack.cs
@@ -29,14 +29,11 @@ public class Stack : IEnumerable,
private Object _syncRoot;
private const int DefaultCapacity = 4;
- private static T[] s_emptyArray = Array.Empty();
///
public Stack()
{
- _array = s_emptyArray;
- _size = 0;
- _version = 0;
+ _array = Array.Empty();
}
// Create a stack with a specific initial capacity. The initial capacity
@@ -47,8 +44,6 @@ public Stack(int capacity)
if (capacity < 0)
throw new ArgumentOutOfRangeException("capacity", SR.ArgumentOutOfRange_NeedNonNegNumRequired);
_array = new T[capacity];
- _size = 0;
- _version = 0;
}
// Fills a Stack with the contents of a particular collection. The items are
@@ -59,28 +54,7 @@ public Stack(IEnumerable collection)
{
if (collection == null)
throw new ArgumentNullException("collection");
-
- ICollection c = collection as ICollection;
- if (c != null)
- {
- int count = c.Count;
- _array = new T[count];
- c.CopyTo(_array, 0);
- _size = count;
- }
- else
- {
- _size = 0;
- _array = new T[DefaultCapacity];
-
- using (IEnumerator en = collection.GetEnumerator())
- {
- while (en.MoveNext())
- {
- Push(en.Current);
- }
- }
- }
+ _array = EnumerableHelpers.ToArray(collection, out _size);
}
///