diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.cs index c6b6f3140e4cc2..3f71a5eed835e3 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.cs @@ -285,7 +285,7 @@ public bool SetEquals(IEnumerable other) return true; } - return SetEquals(other, this.Origin); + return SetEquals(other, this.Origin); } /// @@ -743,19 +743,102 @@ private static bool SetEquals(IEnumerable other, MutationInput origin) { Requires.NotNull(other, nameof(other)); + if (other is 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 < origin.Count) + { + return false; + } + + switch (other) + { + case ImmutableHashSet otherAsImmutableHashSet: + if (otherAsImmutableHashSet.Count != origin.Count) + { + return false; + } + + if (origin.EqualityComparer.Equals(otherAsImmutableHashSet.KeyComparer)) + { + return SetEqualsWithImmutableHashset(otherAsImmutableHashSet, origin); + } + break; + + case HashSet otherAsHashset: + if (otherAsHashset.Count != origin.Count) + { + return false; + } + + if (origin.EqualityComparer.Equals(otherAsHashset.Comparer)) + { + return SetEqualsWithHashset(otherAsHashset, origin); + } + break; + } + } + + else if (other is ICollection otherAsICollection && otherAsICollection.Count < origin.Count) + { + return false; + } + var otherSet = new HashSet(other, origin.EqualityComparer); - if (origin.Count != otherSet.Count) + if (otherSet.Count != origin.Count) { return false; } - foreach (T item in otherSet) + return SetEqualsWithHashset(otherSet, origin); + } + + /// + /// Performs the set operation on a given data structure. + /// Note: + /// 1. Count equality must be verified by the caller before calling this method, + /// as it is invoked by multiple callers with different validation logic. + /// 2. We iterate over 'origin' because it is often the smaller collection + /// for some callers, allowing the loop to terminate as quickly as possible + /// + private static bool SetEqualsWithImmutableHashset(ImmutableHashSet other, MutationInput origin) + { + Requires.NotNull(other, nameof(other)); + + using var e = new ImmutableHashSet.Enumerator(origin.Root); + while (e.MoveNext()) { - if (!Contains(item, origin)) + if (!other.Contains(e.Current)) { return false; } } + + return true; + } + + /// + /// Performs the set operation on a given data structure. + /// Note: + /// 1. Count equality must be verified by the caller before calling this method, + /// as it is invoked by multiple callers with different validation logic. + /// 2. Iterating over 'origin' ensures O(N) complexity via 'other's efficient lookups + /// (avoiding O(N log N)) and enables faster termination as + /// the smaller collection for some callers. + /// + private static bool SetEqualsWithHashset(HashSet other, MutationInput origin) + { + Requires.NotNull(other, nameof(other)); + + using var e = new ImmutableHashSet.Enumerator(origin.Root); + while (e.MoveNext()) + { + if (!other.Contains(e.Current)) + { + return false; + } + } + return true; }