diff --git a/src/libraries/System.Collections/tests/Generic/HashSet/HashSet.Generic.Tests.cs b/src/libraries/System.Collections/tests/Generic/HashSet/HashSet.Generic.Tests.cs index 25a4803a5d14d5..673fb788101ff7 100644 --- a/src/libraries/System.Collections/tests/Generic/HashSet/HashSet.Generic.Tests.cs +++ b/src/libraries/System.Collections/tests/Generic/HashSet/HashSet.Generic.Tests.cs @@ -907,5 +907,49 @@ static void TestComparerSerialization(IEqualityComparer eq } #endregion + + #region UnionWith + + public static IEnumerable UnionWith_HashSet_TestData() + { + foreach (int count in new[] { 0, 1, 75 }) + { + foreach (bool destinationEmpty in new[] { true, false }) + { + foreach (bool sourceSparseFilled in new[] { true, false }) + { + yield return new object[] { count, destinationEmpty, sourceSparseFilled }; + } + } + } + } + + [Theory] + [MemberData(nameof(UnionWith_HashSet_TestData))] + public void HashSet_Generic_UnionWith_HashSet(int count, bool destinationEmpty, bool sourceSparseFilled) + { + HashSet source = (HashSet)CreateEnumerable(EnumerableType.HashSet, null, count, 0, 0); + + if (sourceSparseFilled) + { + List sourceElements = source.ToList(); + foreach (int i in NonSquares(count)) + source.Remove(sourceElements[i]); + } + + HashSet destination = destinationEmpty + ? new HashSet(source.Comparer) + : (HashSet)GenericISetFactory(1); + + HashSet expected = new HashSet(destination, source.Comparer); + foreach (T item in source) + expected.Add(item); + + destination.UnionWith(source); + + Assert.True(expected.SetEquals(destination)); + } + + #endregion } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs index b7a30cbd904a0e..14cc4bb26b3c67 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs @@ -806,6 +806,14 @@ public void UnionWith(IEnumerable other) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.other); } + // If this set is empty and other is a HashSet with the same effective comparer, + // we can copy the data directly instead of adding each element individually. + if (Count == 0 && other is HashSet otherAsSet && EffectiveEqualityComparersAreEqual(this, otherAsSet)) + { + ConstructFrom(otherAsSet); + return; + } + foreach (T item in other) { AddIfNotPresent(item, out _);