diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs index daaceaefef1ad..5291c2b6a7d00 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs @@ -1,10 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Serialization; namespace System.Collections.Generic @@ -83,7 +85,7 @@ public Dictionary(int capacity, IEqualityComparer? comparer) public Dictionary(IDictionary dictionary) : this(dictionary, null) { } public Dictionary(IDictionary dictionary, IEqualityComparer? comparer) : - this(dictionary != null ? dictionary.Count : 0, comparer) + this(dictionary?.Count ?? 0, comparer) { if (dictionary == null) { @@ -106,15 +108,15 @@ public Dictionary(IEnumerable> collection, IEqualityC AddRange(collection); } - private void AddRange(IEnumerable> collection) + private void AddRange(IEnumerable> enumerable) { - // It is likely that the passed-in dictionary is Dictionary. When this is the case, + // It is likely that the passed-in enumerable is Dictionary. When this is the case, // avoid the enumerator allocation and overhead by looping through the entries array directly. // We only do this when dictionary is Dictionary and not a subclass, to maintain // back-compat with subclasses that may have overridden the enumerator behavior. - if (collection.GetType() == typeof(Dictionary)) + if (enumerable.GetType() == typeof(Dictionary)) { - Dictionary source = (Dictionary)collection; + Dictionary source = (Dictionary)enumerable; if (source.Count == 0) { @@ -150,8 +152,29 @@ private void AddRange(IEnumerable> collection) return; } - // Fallback path for IEnumerable that isn't a non-subclassed Dictionary. - foreach (KeyValuePair pair in collection) + // We similarly special-case KVP<>[] and List>, as they're commonly used to seed dictionaries, and + // we want to avoid the enumerator costs (e.g. allocation) for them as well. Extract a span if possible. + ReadOnlySpan> span; + if (enumerable is KeyValuePair[] array) + { + span = array; + } + else if (enumerable.GetType() == typeof(List>)) + { + span = CollectionsMarshal.AsSpan((List>)enumerable); + } + else + { + // Fallback path for all other enumerables + foreach (KeyValuePair pair in enumerable) + { + Add(pair.Key, pair.Value); + } + return; + } + + // We got a span. Add the elements to the dictionary. + foreach (KeyValuePair pair in span) { Add(pair.Key, pair.Value); }