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