diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs index 902e9168872cf4..0434ace28f7494 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs @@ -375,24 +375,56 @@ public bool SetEquals(IEnumerable other) return true; } - var otherSet = new SortedSet(other, this.KeyComparer); - if (this.Count != otherSet.Count) + switch (other) { - return false; + case ImmutableSortedSet otherAsImmutableSortedSet: + if (EqualityComparer>.Default.Equals(this.KeyComparer, otherAsImmutableSortedSet.KeyComparer)) + { + if (otherAsImmutableSortedSet.Count != this.Count) + { + return false; + } + return SetEqualsWithImmutableSortedSet(otherAsImmutableSortedSet, this); + } + + if (otherAsImmutableSortedSet.Count < this.Count) + { + return false; + } + break; + + case SortedSet otherAsSortedSet: + if (EqualityComparer>.Default.Equals(this.KeyComparer, otherAsSortedSet.Comparer)) + { + if (otherAsSortedSet.Count != this.Count) + { + return false; + } + return SetEqualsWithSortedSet(otherAsSortedSet, this); + } + + if (otherAsSortedSet.Count < this.Count) + { + return false; + } + break; + + case ICollection otherAsICollectionGeneric: + // We check for < instead of != because other is not guaranteed to be a set; it could be a collection with duplicates. + if (otherAsICollectionGeneric.Count < this.Count) + { + return false; + } + break; } - int matches = 0; - foreach (T item in otherSet) + var otherSet = new SortedSet(other, this.KeyComparer); + if (otherSet.Count != this.Count) { - if (!this.Contains(item)) - { - return false; - } - - matches++; + return false; } - return matches == this.Count; + return SetEqualsWithSortedSet(otherSet, this); } /// @@ -1079,6 +1111,42 @@ private ImmutableSortedSet UnionIncremental(ReadOnlySpan items) return this.Wrap(result); } + private static bool SetEqualsWithImmutableSortedSet(ImmutableSortedSet other, ImmutableSortedSet source) + { + // We can use a linear scan because both sets are sorted using the same comparer. + using var e = other.GetEnumerator(); + foreach (T item in source) + { + bool eHasMore = e.MoveNext(); + Debug.Assert(eHasMore); + + if (source.KeyComparer.Compare(item, e.Current) != 0) + { + return false; + } + } + + return true; + } + + private static bool SetEqualsWithSortedSet(SortedSet other, ImmutableSortedSet source) + { + // We can use a linear scan because both sets are sorted using the same comparer. + using var e = other.GetEnumerator(); + foreach (T item in source) + { + bool eHasMore = e.MoveNext(); + Debug.Assert(eHasMore); + + if (source.KeyComparer.Compare(item, e.Current) != 0) + { + return false; + } + } + + return true; + } + /// /// Creates a wrapping collection type around a root node. /// diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs index f16d59dd63ae1b..0a7e7249efec08 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs @@ -77,6 +77,96 @@ public void RandomOperationsTest() } } + [Fact] + public void SetEqualsMismatchedComparersOriginInsensitiveOtherSensitive() + { + var ignoreCaseSet = ImmutableSortedSet.Create(StringComparer.OrdinalIgnoreCase, "a"); + var sensitiveSet = ImmutableSortedSet.Create(StringComparer.Ordinal, "a", "A"); + + Assert.True(ignoreCaseSet.SetEquals(sensitiveSet)); + } + + [Fact] + public void SetEqualsMismatchedComparersOriginSensitiveOtherInsensitive() + { + var sensitiveSetMain = ImmutableSortedSet.Create(StringComparer.Ordinal, "a"); + var insensitiveMutable = new SortedSet(StringComparer.OrdinalIgnoreCase) { "a", "A" }; + + Assert.True(sensitiveSetMain.SetEquals(insensitiveMutable)); + } + + [Fact] + public void SetEqualsICollectionWithDuplicatesValidatesCorrectness() + { + var ignoreCaseSet = ImmutableSortedSet.Create(StringComparer.OrdinalIgnoreCase, "a"); + var listWithDupes = new List { "a", "a", "a", "a" }; + + Assert.True(ignoreCaseSet.SetEquals(listWithDupes)); + } + + [Fact] + public void SetEqualsDifferentContent() + { + var ignoreCaseSet = ImmutableSortedSet.Create(StringComparer.OrdinalIgnoreCase, "a"); + var setB = ImmutableSortedSet.Create(StringComparer.Ordinal, "b"); + + Assert.False(ignoreCaseSet.SetEquals(setB)); + } + + [Fact] + public void SetEqualsMismatchedComparersOtherCountSmaller() + { + var originTwoElements = ImmutableSortedSet.Create(StringComparer.OrdinalIgnoreCase, "a", "b"); + var otherOneElement = ImmutableSortedSet.Create(StringComparer.Ordinal, "a"); + + Assert.False(originTwoElements.SetEquals(otherOneElement)); + } + + [Fact] + public void SetEqualsMatchedComparersDifferentCounts() + { + var matchedSet1 = ImmutableSortedSet.Create(StringComparer.Ordinal, "a", "b"); + var matchedSet2 = ImmutableSortedSet.Create(StringComparer.Ordinal, "a"); + + Assert.False(matchedSet1.SetEquals(matchedSet2)); + } + + [Fact] + public void SetEqualsMatchedComparersSameContent() + { + var matchedSet1 = ImmutableSortedSet.Create(StringComparer.Ordinal, "a", "b"); + var matchedSet2 = ImmutableSortedSet.Create(StringComparer.Ordinal, "a", "b"); + + Assert.True(matchedSet1.SetEquals(matchedSet2)); + } + + [Fact] + public void SetEqualsEmptySetsDifferentComparers() + { + var empty1 = ImmutableSortedSet.Empty.WithComparer(StringComparer.Ordinal); + var empty2 = ImmutableSortedSet.Empty.WithComparer(StringComparer.OrdinalIgnoreCase); + + Assert.True(empty1.SetEquals(empty2)); + } + + [Fact] + public void SetEqualsMismatchedComparersOriginSensitiveOtherInsensitiveSameCount() + { + var sensitiveSet = ImmutableSortedSet.Create(StringComparer.Ordinal, "a", "A"); + var insensitiveSet = ImmutableSortedSet.Create(StringComparer.OrdinalIgnoreCase, "a", "b"); + + Assert.False(sensitiveSet.SetEquals(insensitiveSet)); + } + + [Fact] + public void SetEqualsMismatchedComparersOtherIsLarger() + { + var origin = ImmutableSortedSet.Create(StringComparer.OrdinalIgnoreCase, "a"); + var other = ImmutableSortedSet.Create(StringComparer.Ordinal, "a", "b"); + + Assert.False(origin.SetEquals(other)); + } + [Fact] public void CustomSort() {