Skip to content

Commit

Permalink
Rewrite HashSet<T>'s implementation based on Dictionary<T>'s (#37180)
Browse files Browse the repository at this point in the history
* Move HashSet to Corelib

* Small style cleanups in Dictionary

And factor out InsertionBehavior into its own file

* Rewrite HashSet based on Dictionary's implementation

This effectively deletes HashSet's data structure and replaces it with the one used by Dictionary, then updated for the differences (e.g. just a value rather than a key and a value).  HashSet used to have the same implementation, but Dictionary has evolved significantly and HashSet hasn't; this brings them to basic parity on implementation.

Based on perf tests, I veered away from Dictionary's implementation in a few places (e.g. a goto-based implementation in the core find method led to a significant regression for Int32-based Contains operations), and we should follow-up to understand whether Dictionary should be changed as well, or why there's a difference between the two.

Functionally, bringing over Dictionary's implementation yields a few notable changes, namely that Remove and Clear no longer invalidate enumerations.  The tests have been updated accordingly.

* Make a corresponding cleanup change to Dictionary

* Use HashSet in Corelib

* Address PR feedback

* Clean up HashSetEqualityComparer

* Port Dictionary's comparer serialization test to HashSet

* Address PR feedback
  • Loading branch information
stephentoub authored May 31, 2020
1 parent e250030 commit 262948a
Show file tree
Hide file tree
Showing 15 changed files with 2,003 additions and 2,399 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ private RuntimeType[] PopulateInterfaces(Filter filter)
}
else
{
List<RuntimeType?> al = new List<RuntimeType?>();
var al = new HashSet<RuntimeType>();

// Get all constraints
Type[] constraints = declaringType.GetGenericParameterConstraints();
Expand All @@ -1003,31 +1003,16 @@ private RuntimeType[] PopulateInterfaces(Filter filter)

Type[] temp = constraint.GetInterfaces();
for (int j = 0; j < temp.Length; j++)
al.Add(temp[j] as RuntimeType);
al.Add((RuntimeType)temp[j]);
}

// Remove duplicates
Dictionary<RuntimeType, RuntimeType> ht = new Dictionary<RuntimeType, RuntimeType>();
for (int i = 0; i < al.Count; i++)
// Populate list, without duplicates
foreach (RuntimeType rt in al)
{
RuntimeType constraint = al[i]!;
if (!ht.ContainsKey(constraint))
ht[constraint] = constraint;
}

RuntimeType[] interfaces = new RuntimeType[ht.Values.Count];
ht.Values.CopyTo(interfaces, 0);

// Populate link-list
for (int i = 0; i < interfaces.Length; i++)
{
if (filter.RequiresStringComparison())
if (!filter.RequiresStringComparison() || filter.Match(RuntimeTypeHandle.GetUtf8Name(rt)))
{
if (!filter.Match(RuntimeTypeHandle.GetUtf8Name(interfaces[i])))
continue;
list.Add(rt);
}

list.Add(interfaces[i]);
}
}

Expand Down
3 changes: 0 additions & 3 deletions src/libraries/System.Collections/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,6 @@
<data name="Arg_BitArrayTypeUnsupported" xml:space="preserve">
<value>Only supported array types for CopyTo on BitArrays are Boolean[], Int32[] and Byte[].</value>
</data>
<data name="Arg_HSCapacityOverflow" xml:space="preserve">
<value>HashSet capacity is too big.</value>
</data>
<data name="Arg_HTCapacityOverflow" xml:space="preserve">
<value>Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@
</ItemGroup>
<ItemGroup>
<Compile Include="System\Collections\BitArray.cs" />
<Compile Include="System\Collections\Generic\BitHelper.cs" />
<Compile Include="System\Collections\Generic\CollectionExtensions.cs" />
<Compile Include="$(CoreLibSharedDir)System\Collections\Generic\ICollectionDebugView.cs"
Link="Common\System\Collections\Generic\ICollectionDebugView.cs" />
<Compile Include="$(CoreLibSharedDir)System\Collections\Generic\IDictionaryDebugView.cs"
Link="Common\System\Collections\Generic\IDictionaryDebugView.cs" />
<Compile Include="System\Collections\Generic\HashSet.cs" />
<Compile Include="System\Collections\Generic\HashSetEqualityComparer.cs" />
<Compile Include="System\Collections\Generic\LinkedList.cs" />
<Compile Include="System\Collections\Generic\Queue.cs" />
<Compile Include="System\Collections\Generic\QueueDebugView.cs" />
Expand All @@ -33,6 +30,8 @@
<!-- Shared Common -->
<Compile Include="$(CoreLibSharedDir)System\Collections\HashHelpers.cs"
Link="Common\System\Collections\HashHelpers.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\BitHelper.cs"
Link="Common\System\Collections\Generic\BitHelper.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\EnumerableHelpers.cs"
Link="Common\System\Collections\Generic\EnumerableHelpers.cs" />
</ItemGroup>
Expand Down
Loading

0 comments on commit 262948a

Please sign in to comment.