Skip to content

Commit 5ecd83a

Browse files
authored
Avoid intermediate array allocation when the collection is first created (#11799)
In Framework, `List<T>.AddRange()` has a specialization for `ICollection<T>` where it allocates an intermediate array to copy the contents of the collection. It then copies the contents of the intermediate array to the inner list. public void AddRange(IEnumerable<T> collection) { InsertRange(_size, collection); } ... public void InsertRange(int index, IEnumerable<T> collection) { if (collection == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); } if ((uint)index > (uint)_size) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); } if (collection is ICollection<T> { Count: var count } collection2) { if (count > 0) { EnsureCapacity(_size + count); if (index < _size) { Array.Copy(_items, index, _items, index + count, _size - index); } if (this == collection2) { Array.Copy(_items, 0, _items, index, index); Array.Copy(_items, index + count, _items, index * 2, _size - index); } else { T[] array = new T[count]; // Intermediate array allocation here collection2.CopyTo(array, 0); // copying to the intermediate array array.CopyTo(_items, index); // copying from the intermediate array to the inner list } _size += count; } } else { using IEnumerator<T> enumerator = collection.GetEnumerator(); while (enumerator.MoveNext()) { Insert(index++, enumerator.Current); } } _version++; } The constructor just allocates the internal array and does the copy once public List(IEnumerable<T> collection) { if (collection == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); } if (collection is ICollection<T> { Count: var count } collection2) { if (count == 0) { _items = _emptyArray; return; } _items = new T[count]; collection2.CopyTo(_items, 0); _size = count; return; } _size = 0; _items = _emptyArray; foreach (T item in collection) { Add(item); } } Fixes # ### Context ### Changes Made ### Testing ### Notes
2 parents cb7e284 + de5ee67 commit 5ecd83a

File tree

1 file changed

+17
-4
lines changed
  • src/Build/BackEnd/Components/RequestBuilder

1 file changed

+17
-4
lines changed

src/Build/BackEnd/Components/RequestBuilder/Lookup.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,15 @@ public ICollection<ProjectItemInstance> GetItems(string itemType)
480480
ICollection<ProjectItemInstance> adds = scope.Adds[itemType];
481481
if (adds.Count != 0)
482482
{
483-
allAdds ??= new List<ProjectItemInstance>(adds.Count);
484-
allAdds.AddRange(adds);
483+
if (allAdds == null)
484+
{
485+
// Use the List<T>(IEnumerable<T>) constructor to avoid an intermediate array allocation.
486+
allAdds = new List<ProjectItemInstance>(adds);
487+
}
488+
else
489+
{
490+
allAdds.AddRange(adds);
491+
}
485492
}
486493
}
487494

@@ -491,8 +498,14 @@ public ICollection<ProjectItemInstance> GetItems(string itemType)
491498
ICollection<ProjectItemInstance> removes = scope.Removes[itemType];
492499
if (removes.Count != 0)
493500
{
494-
allRemoves ??= new List<ProjectItemInstance>(removes.Count);
495-
allRemoves.AddRange(removes);
501+
if (allRemoves == null)
502+
{
503+
allRemoves = new List<ProjectItemInstance>(removes);
504+
}
505+
else
506+
{
507+
allRemoves.AddRange(removes);
508+
}
496509
}
497510
}
498511

0 commit comments

Comments
 (0)