// Constructs a List with a given initial capacity. The list is // initially empty, but will have room for the given number of elements // before any reallocations are required. // public LowLevelList(int capacity) { if (capacity < 0) { throw new ArgumentOutOfRangeException("capacity"); } Contract.EndContractBlock(); if (capacity == 0) { _items = Array_.Empty <T>(); } else { _items = new T[capacity]; } }
// Constructs a List, copying the contents of the given collection. The // size and capacity of the new list will both be equal to the size of the // given collection. // public LowLevelList(IEnumerable <T> collection) { if (collection == null) { throw new ArgumentNullException("collection"); } Contract.EndContractBlock(); ICollection <T> c = collection as ICollection <T>; if (c != null) { int count = c.Count; if (count == 0) { _items = Array_.Empty <T>(); } else { _items = new T[count]; c.CopyTo(_items, 0); _size = count; } } else { _size = 0; _items = Array_.Empty <T>(); // This enumerable could be empty. Let Add allocate a new array, if needed. // Note it will also go to _defaultCapacity first, not 1, then 2, etc. using (IEnumerator <T> en = collection.GetEnumerator()) { while (en.MoveNext()) { Add(en.Current); } } } }
// Constructs a List. The list is initially empty and has a capacity // of zero. Upon adding the first element to the list the capacity is // increased to 16, and then increased in multiples of two as required. public LowLevelList() { _items = Array_.Empty <T>(); }
/// <summary>Converts an enumerable to an array using the same logic as does List{T}.</summary> /// <param name="length">The number of items stored in the resulting array, 0-indexed.</param> /// <returns> /// The resulting array. The length of the array may be greater than <paramref name="length"/>, /// which is the actual number of elements in the array. /// </returns> internal static T[] ToArray <T>(IEnumerable <T> source, out int length) { T[] arr; int count = 0; ICollection <T> ic = source as ICollection <T>; if (ic != null) { count = ic.Count; if (count == 0) { arr = Array_.Empty <T>(); } 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<T>. 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<T>, which as of .NET 4.6 is just ConcurrentDictionary<TKey, TValue>. arr = new T[count]; ic.CopyTo(arr, 0); } } else { arr = Array_.Empty <T>(); 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<T>: // 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; } Array.Resize(ref arr, newLength); } arr[count++] = item; } } length = count; return(arr); }