Skip to content

Commit 69dd454

Browse files
authored
Add ImmutableDictionary.CreateRangeWithOverwrite (#115570)
1 parent 3197765 commit 69dd454

File tree

4 files changed

+107
-0
lines changed

4 files changed

+107
-0
lines changed

src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,9 @@ public static partial class ImmutableDictionary
436436
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue>.Builder CreateBuilder<TKey, TValue>(System.Collections.Generic.IEqualityComparer<TKey>? keyComparer) where TKey : notnull { throw null; }
437437
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue>.Builder CreateBuilder<TKey, TValue>(System.Collections.Generic.IEqualityComparer<TKey>? keyComparer, System.Collections.Generic.IEqualityComparer<TValue>? valueComparer) where TKey : notnull { throw null; }
438438
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue> CreateRange<TKey, TValue>(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>> items) where TKey : notnull { throw null; }
439+
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue> CreateRangeWithOverwrite<TKey, TValue>(params System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<TKey, TValue>> items) where TKey : notnull { throw null; }
439440
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue> CreateRange<TKey, TValue>(System.Collections.Generic.IEqualityComparer<TKey>? keyComparer, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>> items) where TKey : notnull { throw null; }
441+
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue> CreateRangeWithOverwrite<TKey, TValue>(System.Collections.Generic.IEqualityComparer<TKey>? keyComparer, params System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<TKey, TValue>> items) where TKey : notnull { throw null; }
440442
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue> CreateRange<TKey, TValue>(System.Collections.Generic.IEqualityComparer<TKey>? keyComparer, System.Collections.Generic.IEqualityComparer<TValue>? valueComparer, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>> items) where TKey : notnull { throw null; }
441443
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue> Create<TKey, TValue>() where TKey : notnull { throw null; }
442444
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue> Create<TKey, TValue>(System.Collections.Generic.IEqualityComparer<TKey>? keyComparer) where TKey : notnull { throw null; }
@@ -453,6 +455,7 @@ public static partial class ImmutableDictionary
453455
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue> ToImmutableDictionary<TSource, TKey, TValue>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Func<TSource, TValue> elementSelector, System.Collections.Generic.IEqualityComparer<TKey>? keyComparer) where TKey : notnull { throw null; }
454456
public static System.Collections.Immutable.ImmutableDictionary<TKey, TValue> ToImmutableDictionary<TSource, TKey, TValue>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Func<TSource, TValue> elementSelector, System.Collections.Generic.IEqualityComparer<TKey>? keyComparer, System.Collections.Generic.IEqualityComparer<TValue>? valueComparer) where TKey : notnull { throw null; }
455457
}
458+
[System.Runtime.CompilerServices.CollectionBuilderAttribute(typeof(System.Collections.Immutable.ImmutableDictionary), "CreateRangeWithOverwrite")]
456459
public sealed partial class ImmutableDictionary<TKey, TValue> : System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey, TValue>>, System.Collections.Generic.IDictionary<TKey, TValue>, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>>, System.Collections.Generic.IReadOnlyCollection<System.Collections.Generic.KeyValuePair<TKey, TValue>>, System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>, System.Collections.ICollection, System.Collections.IDictionary, System.Collections.IEnumerable, System.Collections.Immutable.IImmutableDictionary<TKey, TValue> where TKey : notnull
457460
{
458461
internal ImmutableDictionary() { }

src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,25 @@ public static ImmutableDictionary<TKey, TValue> CreateRange<TKey, TValue>(IEnume
6464
return ImmutableDictionary<TKey, TValue>.Empty.AddRange(items);
6565
}
6666

67+
/// <summary>
68+
/// Creates a new immutable dictionary that contains the specified items.
69+
/// </summary>
70+
/// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
71+
/// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
72+
/// <param name="items">The items used to populate the dictionary before it's immutable.</param>
73+
/// <returns>A new immutable dictionary that contains the specified items.</returns>
74+
/// <remarks>
75+
/// In contrast to <see cref="CreateRange{TKey, TValue}(IEnumerable{KeyValuePair{TKey, TValue}})"/>,
76+
/// if there are duplicate keys in the <paramref name="items"/> collection, the last one will be used,
77+
/// rather than an exception being thrown.
78+
/// </remarks>
79+
public static ImmutableDictionary<TKey, TValue> CreateRangeWithOverwrite<TKey, TValue>(
80+
params ReadOnlySpan<KeyValuePair<TKey, TValue>> items)
81+
where TKey : notnull
82+
{
83+
return ImmutableDictionary<TKey, TValue>.Empty.AddRange(items, ImmutableDictionary<TKey, TValue>.KeyCollisionBehavior.SetValue);
84+
}
85+
6786
/// <summary>
6887
/// Creates a new immutable collection prefilled with the specified items.
6988
/// </summary>
@@ -77,6 +96,27 @@ public static ImmutableDictionary<TKey, TValue> CreateRange<TKey, TValue>(IEqual
7796
return ImmutableDictionary<TKey, TValue>.Empty.WithComparers(keyComparer).AddRange(items);
7897
}
7998

99+
/// <summary>
100+
/// Creates a new immutable dictionary that contains the specified items and uses the specified key comparer.
101+
/// </summary>
102+
/// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
103+
/// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
104+
/// <param name="keyComparer">The comparer implementation to use to compare keys for equality.</param>
105+
/// <param name="items">The items to add to the dictionary before it's immutable.</param>
106+
/// <returns>A new immutable dictionary that contains the specified items and uses the specified comparer.</returns>
107+
/// <remarks>
108+
/// In contrast to <see cref="CreateRange{TKey, TValue}(IEqualityComparer{TKey}?, IEnumerable{KeyValuePair{TKey, TValue}})"/>,
109+
/// if there are duplicate keys in the <paramref name="items"/> collection, the last one will be used,
110+
/// rather than an exception being thrown.
111+
/// </remarks>
112+
public static ImmutableDictionary<TKey, TValue> CreateRangeWithOverwrite<TKey, TValue>(
113+
IEqualityComparer<TKey>? keyComparer,
114+
params ReadOnlySpan<KeyValuePair<TKey, TValue>> items)
115+
where TKey : notnull
116+
{
117+
return ImmutableDictionary<TKey, TValue>.Empty.WithComparers(keyComparer).AddRange(items, ImmutableDictionary<TKey, TValue>.KeyCollisionBehavior.SetValue);
118+
}
119+
80120
/// <summary>
81121
/// Creates a new immutable collection prefilled with the specified items.
82122
/// </summary>

src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics;
66
using System.Diagnostics.CodeAnalysis;
77
using System.Linq;
8+
using System.Runtime.CompilerServices;
89

910
namespace System.Collections.Immutable
1011
{
@@ -15,6 +16,7 @@ namespace System.Collections.Immutable
1516
/// <typeparam name="TValue">The type of the value.</typeparam>
1617
[DebuggerDisplay("Count = {Count}")]
1718
[DebuggerTypeProxy(typeof(ImmutableDictionaryDebuggerProxy<,>))]
19+
[CollectionBuilder(typeof(ImmutableDictionary), nameof(ImmutableDictionary.CreateRangeWithOverwrite))]
1820
public sealed partial class ImmutableDictionary<TKey, TValue> : IImmutableDictionary<TKey, TValue>, IImmutableDictionaryInternal<TKey, TValue>, IDictionary<TKey, TValue>, IDictionary where TKey : notnull
1921
{
2022
/// <summary>
@@ -313,6 +315,11 @@ public ImmutableDictionary<TKey, TValue> AddRange(IEnumerable<KeyValuePair<TKey,
313315
return this.AddRange(pairs, false);
314316
}
315317

318+
internal ImmutableDictionary<TKey, TValue> AddRange(ReadOnlySpan<KeyValuePair<TKey, TValue>> pairs, KeyCollisionBehavior collisionBehavior = KeyCollisionBehavior.ThrowIfValueDifferent)
319+
{
320+
return AddRange(pairs, this.Origin, collisionBehavior).Finalize(this);
321+
}
322+
316323
/// <summary>
317324
/// See the <see cref="IImmutableDictionary{TKey, TValue}"/> interface.
318325
/// </summary>
@@ -970,6 +977,30 @@ private static MutationResult AddRange(IEnumerable<KeyValuePair<TKey, TValue>> i
970977
return new MutationResult(newRoot, countAdjustment);
971978
}
972979

980+
/// <summary>
981+
/// Performs the operation on a given data structure.
982+
/// </summary>
983+
private static MutationResult AddRange(ReadOnlySpan<KeyValuePair<TKey, TValue>> items, MutationInput origin, KeyCollisionBehavior collisionBehavior = KeyCollisionBehavior.ThrowIfValueDifferent)
984+
{
985+
int countAdjustment = 0;
986+
SortedInt32KeyNode<ImmutableDictionary<TKey, TValue>.HashBucket> newRoot = origin.Root;
987+
foreach (KeyValuePair<TKey, TValue> pair in items)
988+
{
989+
Requires.NotNullAllowStructs(pair.Key, nameof(pair.Key));
990+
int hashCode = origin.KeyComparer.GetHashCode(pair.Key);
991+
HashBucket bucket = newRoot.GetValueOrDefault(hashCode);
992+
OperationResult result;
993+
ImmutableDictionary<TKey, TValue>.HashBucket newBucket = bucket.Add(pair.Key, pair.Value, origin.KeyOnlyComparer, origin.ValueComparer, collisionBehavior, out result);
994+
newRoot = UpdateRoot(newRoot, hashCode, newBucket, origin.HashBucketComparer);
995+
if (result == OperationResult.SizeChanged)
996+
{
997+
countAdjustment++;
998+
}
999+
}
1000+
1001+
return new MutationResult(newRoot, countAdjustment);
1002+
}
1003+
9731004
/// <summary>
9741005
/// Performs the operation on a given data structure.
9751006
/// </summary>

src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTest.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ public void ContainsValue_NoSuchValue_ReturnsFalse()
121121
public void Create()
122122
{
123123
IEnumerable<KeyValuePair<string, string>> pairs = new Dictionary<string, string> { { "a", "b" } };
124+
ReadOnlySpan<KeyValuePair<string, string>> pairsWithDuplicates =
125+
[
126+
new KeyValuePair<string, string>("a", "b"),
127+
new KeyValuePair<string, string>("a", "c"),
128+
new KeyValuePair<string, string>("b", "d"),
129+
new KeyValuePair<string, string>("B", "e"),
130+
new KeyValuePair<string, string>("a", "f"),
131+
];
124132
StringComparer keyComparer = StringComparer.OrdinalIgnoreCase;
125133
StringComparer valueComparer = StringComparer.CurrentCulture;
126134

@@ -153,6 +161,31 @@ public void Create()
153161
Assert.Equal(1, dictionary.Count);
154162
Assert.Same(keyComparer, dictionary.KeyComparer);
155163
Assert.Same(valueComparer, dictionary.ValueComparer);
164+
165+
dictionary = ImmutableDictionary.CreateRangeWithOverwrite<string, string>();
166+
Assert.Equal(0, dictionary.Count);
167+
Assert.Same(EqualityComparer<string>.Default, dictionary.KeyComparer);
168+
Assert.Same(EqualityComparer<string>.Default, dictionary.ValueComparer);
169+
170+
dictionary = ImmutableDictionary.CreateRangeWithOverwrite<string, string>(keyComparer);
171+
Assert.Equal(0, dictionary.Count);
172+
Assert.Same(keyComparer, dictionary.KeyComparer);
173+
Assert.Same(EqualityComparer<string>.Default, dictionary.ValueComparer);
174+
175+
dictionary = ImmutableDictionary.CreateRangeWithOverwrite(pairsWithDuplicates);
176+
Assert.Equal(3, dictionary.Count);
177+
Assert.Same(EqualityComparer<string>.Default, dictionary.KeyComparer);
178+
Assert.Same(EqualityComparer<string>.Default, dictionary.ValueComparer);
179+
Assert.Equal("f", dictionary["a"]);
180+
Assert.Equal("d", dictionary["b"]);
181+
Assert.Equal("e", dictionary["B"]);
182+
183+
dictionary = ImmutableDictionary.CreateRangeWithOverwrite(keyComparer, pairsWithDuplicates);
184+
Assert.Equal(2, dictionary.Count);
185+
Assert.Same(keyComparer, dictionary.KeyComparer);
186+
Assert.Same(EqualityComparer<string>.Default, dictionary.ValueComparer);
187+
Assert.Equal("f", dictionary["a"]);
188+
Assert.Equal("e", dictionary["b"]);
156189
}
157190

158191
[Fact]

0 commit comments

Comments
 (0)