Skip to content

Commit

Permalink
Improve fast-return for HashSet SubSet and SetEquals methods (#102758)
Browse files Browse the repository at this point in the history
Co-authored-by: Stephen Toub <stoub@microsoft.com>
  • Loading branch information
lilinus and stephentoub authored Jul 10, 2024
1 parent 5f9fff0 commit f6d12a1
Showing 1 changed file with 38 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -879,25 +879,26 @@ public bool IsSubsetOf(IEnumerable<T> other)
}

// The empty set is a subset of any set, and a set is a subset of itself.
// Set is always a subset of itself
// Set is always a subset of itself.
if (Count == 0 || other == this)
{
return true;
}

// Faster if other has unique elements according to this equality comparer; so check
// that other is a hashset using the same equality comparer.
if (other is HashSet<T> otherAsSet && EqualityComparersAreEqual(this, otherAsSet))
if (other is ICollection<T> otherAsCollection)
{
// if this has more elements then it can't be a subset
if (Count > otherAsSet.Count)
// If this has more elements then it can't be a subset.
if (Count > otherAsCollection.Count)
{
return false;
}

// already checked that we're using same equality comparer. simply check that
// each element in this is contained in other.
return IsSubsetOfHashSetWithSameComparer(otherAsSet);
// Faster if other has unique elements according to this equality comparer; so check
// that other is a hashset using the same equality comparer.
if (other is HashSet<T> otherAsSet && EqualityComparersAreEqual(this, otherAsSet))
{
return IsSubsetOfHashSetWithSameComparer(otherAsSet);
}
}

(int uniqueCount, int unfoundCount) = CheckUniqueAndUnfoundElements(other, returnIfUnfound: false);
Expand All @@ -922,26 +923,22 @@ public bool IsProperSubsetOf(IEnumerable<T> other)

if (other is ICollection<T> otherAsCollection)
{
// No set is a proper subset of an empty set.
if (otherAsCollection.Count == 0)
// No set is a proper subset of a set with less or equal number of elements.
if (otherAsCollection.Count <= Count)
{
return false;
}

// The empty set is a proper subset of anything but the empty set.
if (Count == 0)
{
return otherAsCollection.Count > 0;
// Based on check above, other is not empty when Count == 0.
return true;
}

// Faster if other is a hashset (and we're using same equality comparer).
if (other is HashSet<T> otherAsSet && EqualityComparersAreEqual(this, otherAsSet))
{
if (Count >= otherAsSet.Count)
{
return false;
}

// This has strictly less than number of items in other, so the following
// check suffices for proper subset.
return IsSubsetOfHashSetWithSameComparer(otherAsSet);
Expand Down Expand Up @@ -1088,33 +1085,38 @@ public bool SetEquals(IEnumerable<T> other)
return true;
}

// Faster if other is a hashset and we're using same equality comparer.
if (other is HashSet<T> otherAsSet && EqualityComparersAreEqual(this, otherAsSet))
if (other is ICollection<T> otherAsCollection)
{
// Attempt to return early: since both contain unique elements, if they have
// different counts, then they can't be equal.
if (Count != otherAsSet.Count)
// If this is empty, they are equal iff other is empty.
if (Count == 0)
{
return false;
return otherAsCollection.Count == 0;
}

// Already confirmed that the sets have the same number of distinct elements, so if
// one is a subset of the other then they must be equal.
return IsSubsetOfHashSetWithSameComparer(otherAsSet);
}
else
{
// If this count is 0 but other contains at least one element, they can't be equal.
if (Count == 0 &&
other is ICollection<T> otherAsCollection &&
otherAsCollection.Count > 0)
// Faster if other is a hashset and we're using same equality comparer.
if (other is HashSet<T> otherAsSet && EqualityComparersAreEqual(this, otherAsSet))
{
return false;
// Attempt to return early: since both contain unique elements, if they have
// different counts, then they can't be equal.
if (Count != otherAsSet.Count)
{
return false;
}

// Already confirmed that the sets have the same number of distinct elements, so if
// one is a subset of the other then they must be equal.
return IsSubsetOfHashSetWithSameComparer(otherAsSet);
}

(int uniqueCount, int unfoundCount) = CheckUniqueAndUnfoundElements(other, returnIfUnfound: true);
return uniqueCount == Count && unfoundCount == 0;
// Can't be equal if other set contains fewer elements than this.
if (Count > otherAsCollection.Count)
{
return false;
}
}

(int uniqueCount, int unfoundCount) = CheckUniqueAndUnfoundElements(other, returnIfUnfound: true);
return uniqueCount == Count && unfoundCount == 0;
}

public void CopyTo(T[] array) => CopyTo(array, 0, Count);
Expand Down

0 comments on commit f6d12a1

Please sign in to comment.