11package dotty .tools .dotc .util
22
33/** A hash set that allows some privileged protected access to its internals
4+ * @param initialCapacity Indicates the initial number of slots in the hash table.
5+ * The actual number of slots is always a power of 2, so the
6+ * initial size of the table will be the smallest power of two
7+ * that is equal or greater than the given `initialCapacity`.
8+ * @param loadFactor The maximum fraction of used elements relative to capacity.
9+ * The hash table will be re-sized once the number of elements exceeds
10+ * the current size of the hash table multiplied by loadFactor.
11+ * With the defaults given, the first resize of the table happens once the number of elements
12+ * grows beyond 16.
413 */
5- class HashSet [T >: Null <: AnyRef ](powerOfTwoInitialCapacity : Int = 16 , loadFactor : Float = 0.25f ) extends MutableSet [T ] {
14+ class HashSet [T >: Null <: AnyRef ](initialCapacity : Int = 16 , loadFactor : Float = 0.25f ) extends MutableSet [T ] {
615 private var used : Int = _
716 private var limit : Int = _
817 private var table : Array [AnyRef ] = _
918
10- assert(Integer .bitCount(powerOfTwoInitialCapacity) == 1 )
19+ private def roundToPower (n : Int ) =
20+ if Integer .bitCount(n) == 1 then n
21+ else
22+ def recur (n : Int ): Int =
23+ if n == 1 then 2
24+ else recur(n >>> 1 ) << 1
25+ recur(n)
26+
1127 protected def isEqual (x : T , y : T ): Boolean = x.equals(y)
1228
1329 // Counters for Stats
@@ -27,7 +43,7 @@ class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFact
2743 /** Remove all elements from this set and set back to initial configuration */
2844 def clear (): Unit = {
2945 used = 0
30- allocate(powerOfTwoInitialCapacity )
46+ allocate(roundToPower(initialCapacity) )
3147 }
3248
3349 /** Turn hashcode `x` into a table index */
@@ -36,7 +52,7 @@ class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFact
3652 /** Hashcode, can be overridden */
3753 def hash (x : T ): Int = x.hashCode
3854
39- private def entryAt (idx : Int ) = table.apply (idx).asInstanceOf [T ]
55+ private def entryAt (idx : Int ) = table(idx).asInstanceOf [T ]
4056
4157 /** Find entry such that `isEqual(x, entry)`. If it exists, return it.
4258 * If not, enter `x` in set and return `x`.
@@ -63,7 +79,7 @@ class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFact
6379 }
6480
6581 /** The entry in the set such that `isEqual(x, entry)`, or else `null`. */
66- def findEntry (x : T ): T = {
82+ def lookup (x : T ): T = {
6783 if (Stats .enabled) accesses += 1
6884 var h = index(hash(x))
6985 var entry = entryAt(h)
@@ -77,38 +93,6 @@ class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFact
7793
7894 private var rover : Int = - 1
7995
80- /** Add entry `x` to set */
81- def addEntry (x : T ): Unit = {
82- if (Stats .enabled) accesses += 1
83- var h = index(hash(x))
84- var entry = entryAt(h)
85- while (entry ne null ) {
86- if (isEqual(x, entry)) return
87- if (Stats .enabled) misses += 1
88- h = index(h + 1 )
89- entry = entryAt(h)
90- }
91- table(h) = x
92- used += 1
93- if (used > (table.length >> 2 )) growTable()
94- }
95-
96- /** Add all entries in `xs` to set */
97- def addEntries (xs : TraversableOnce [T ]): Unit =
98- xs.iterator foreach addEntry
99-
100- /** The iterator of all elements in the set */
101- def iterator : Iterator [T ] = new Iterator [T ] {
102- private var i = 0
103- def hasNext : Boolean = {
104- while (i < table.length && (table(i) eq null )) i += 1
105- i < table.length
106- }
107- def next (): T =
108- if (hasNext) { i += 1 ; table(i - 1 ).asInstanceOf [T ] }
109- else null
110- }
111-
11296 /** Privileged access: Find first entry with given hashcode */
11397 protected def findEntryByHash (hashCode : Int ): T = {
11498 rover = index(hashCode)
@@ -158,5 +142,58 @@ class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFact
158142 }
159143 }
160144
145+ /** Add entry `x` to set */
146+ def += (x : T ): Unit = {
147+ if (Stats .enabled) accesses += 1
148+ var h = index(hash(x))
149+ var entry = entryAt(h)
150+ while (entry ne null ) {
151+ if (isEqual(x, entry)) return
152+ if (Stats .enabled) misses += 1
153+ h = index(h + 1 )
154+ entry = entryAt(h)
155+ }
156+ table(h) = x
157+ used += 1
158+ if (used > (table.length >> 2 )) growTable()
159+ }
160+
161+ def -= (x : T ): Unit =
162+ if (Stats .enabled) accesses += 1
163+ var h = index(hash(x))
164+ var entry = entryAt(h)
165+ while entry != null do
166+ if isEqual(x, entry) then
167+ var hole = h
168+ while
169+ h = index(h + 1 )
170+ entry = entryAt(h)
171+ entry != null && index(hash(entry)) != h
172+ do
173+ table(hole) = entry
174+ hole = h
175+ table(hole) = null
176+ used -= 1
177+ return
178+ h = index(h + 1 )
179+ entry = entryAt(h)
180+ end -=
181+
182+ /** Add all entries in `xs` to set */
183+ def ++= (xs : IterableOnce [T ]): Unit =
184+ xs.iterator.foreach(this += _)
185+
186+ /** The iterator of all elements in the set */
187+ def iterator : Iterator [T ] = new Iterator [T ] {
188+ private var i = 0
189+ def hasNext : Boolean = {
190+ while (i < table.length && (table(i) eq null )) i += 1
191+ i < table.length
192+ }
193+ def next (): T =
194+ if (hasNext) { i += 1 ; table(i - 1 ).asInstanceOf [T ] }
195+ else null
196+ }
197+
161198 override def toString (): String = " HashSet(%d / %d)" .format(used, table.length)
162199}
0 commit comments