Set.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the Apache 2.0 License.
  3. // See the LICENSE file in the project root for more information.
  4. // Copied from https://github.com/dotnet/corefx/blob/ec2685715b01d12f16b08d0dfa326649b12db8ec/src/System.Linq/src/System/Linq/Set.cs
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Diagnostics.CodeAnalysis;
  8. namespace System.Linq
  9. {
  10. [ExcludeFromCodeCoverage]
  11. internal sealed class Set<TElement>
  12. {
  13. private readonly IEqualityComparer<TElement> _comparer;
  14. private int[] _buckets;
  15. #if DEBUG
  16. private bool _haveRemoved;
  17. #endif
  18. private Slot[] _slots;
  19. public Set(IEqualityComparer<TElement> comparer)
  20. {
  21. _comparer = comparer ?? EqualityComparer<TElement>.Default;
  22. _buckets = new int[7];
  23. _slots = new Slot[7];
  24. }
  25. internal int Count { get; private set; }
  26. // If value is not in set, add it and return true; otherwise return false
  27. public bool Add(TElement value)
  28. {
  29. #if DEBUG
  30. Debug.Assert(!_haveRemoved, "This class is optimized for never calling Add after Remove. If your changes need to do so, undo that optimization.");
  31. #endif
  32. var hashCode = InternalGetHashCode(value);
  33. for (var i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i]._next)
  34. {
  35. if (_slots[i]._hashCode == hashCode && _comparer.Equals(_slots[i]._value, value))
  36. {
  37. return false;
  38. }
  39. }
  40. if (Count == _slots.Length)
  41. {
  42. Resize();
  43. }
  44. var index = Count;
  45. Count++;
  46. var bucket = hashCode % _buckets.Length;
  47. _slots[index]._hashCode = hashCode;
  48. _slots[index]._value = value;
  49. _slots[index]._next = _buckets[bucket] - 1;
  50. _buckets[bucket] = index + 1;
  51. return true;
  52. }
  53. // If value is in set, remove it and return true; otherwise return false
  54. public bool Remove(TElement value)
  55. {
  56. #if DEBUG
  57. _haveRemoved = true;
  58. #endif
  59. var hashCode = InternalGetHashCode(value);
  60. var bucket = hashCode % _buckets.Length;
  61. var last = -1;
  62. for (var i = _buckets[bucket] - 1; i >= 0; last = i, i = _slots[i]._next)
  63. {
  64. if (_slots[i]._hashCode == hashCode && _comparer.Equals(_slots[i]._value, value))
  65. {
  66. if (last < 0)
  67. {
  68. _buckets[bucket] = _slots[i]._next + 1;
  69. }
  70. else
  71. {
  72. _slots[last]._next = _slots[i]._next;
  73. }
  74. _slots[i]._hashCode = -1;
  75. _slots[i]._value = default;
  76. _slots[i]._next = -1;
  77. return true;
  78. }
  79. }
  80. return false;
  81. }
  82. internal int InternalGetHashCode(TElement value)
  83. {
  84. // Handle comparer implementations that throw when passed null
  85. return (value == null) ? 0 : _comparer.GetHashCode(value) & 0x7FFFFFFF;
  86. }
  87. internal TElement[] ToArray()
  88. {
  89. #if DEBUG
  90. Debug.Assert(!_haveRemoved, "Optimized ToArray cannot be called if Remove has been called.");
  91. #endif
  92. var array = new TElement[Count];
  93. for (var i = 0; i != array.Length; ++i)
  94. {
  95. array[i] = _slots[i]._value;
  96. }
  97. return array;
  98. }
  99. internal List<TElement> ToList()
  100. {
  101. #if DEBUG
  102. Debug.Assert(!_haveRemoved, "Optimized ToList cannot be called if Remove has been called.");
  103. #endif
  104. var count = Count;
  105. var list = new List<TElement>(count);
  106. for (var i = 0; i != count; ++i)
  107. {
  108. list.Add(_slots[i]._value);
  109. }
  110. return list;
  111. }
  112. private void Resize()
  113. {
  114. var newSize = checked((Count * 2) + 1);
  115. var newBuckets = new int[newSize];
  116. var newSlots = new Slot[newSize];
  117. Array.Copy(_slots, 0, newSlots, 0, Count);
  118. for (var i = 0; i < Count; i++)
  119. {
  120. var bucket = newSlots[i]._hashCode % newSize;
  121. newSlots[i]._next = newBuckets[bucket] - 1;
  122. newBuckets[bucket] = i + 1;
  123. }
  124. _buckets = newBuckets;
  125. _slots = newSlots;
  126. }
  127. private struct Slot
  128. {
  129. internal int _hashCode;
  130. internal int _next;
  131. internal TElement _value;
  132. }
  133. }
  134. }