Skip to content

Commit 5f065ed

Browse files
committed
LinkedHashMap/LinkedHashSet: review cleanups
* Add `LinkedHashSet.add` override * Mark the private[collection] HashTable as not used
1 parent e69190b commit 5f065ed

File tree

4 files changed

+114
-129
lines changed

4 files changed

+114
-129
lines changed

library/src/scala/collection/mutable/HashTable.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ import java.lang.Integer
3535
*
3636
* @tparam A type of the elements contained in this hash table.
3737
*/
38-
// Was an abstract class, but to simplify the upgrade of the parallel collections I’ve made it a trait
39-
private[collection] /*abstract class*/ trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashUtils[A] {
38+
// Not used in the standard library, but used in scala-parallel-collections
39+
private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashUtils[A] {
4040
// Replacing Entry type parameter by abstract type member here allows to not expose to public
4141
// implementation-specific entry classes such as `DefaultEntry` or `LinkedEntry`.
4242
// However, I'm afraid it's too late now for such breaking change.

library/src/scala/collection/mutable/LinkedHashMap.scala

Lines changed: 56 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import scala.collection.generic.DefaultSerializable
3131
* @define orderDependent
3232
* @define orderDependentFold
3333
*/
34+
@deprecatedInheritance("LinkedHashMap will be made final; use .withDefault for the common use case of computing a default value", "2.13.11")
3435
class LinkedHashMap[K, V]
3536
extends AbstractMap[K, V]
3637
with SeqMap[K, V]
@@ -44,17 +45,23 @@ class LinkedHashMap[K, V]
4445

4546
// stepper / keyStepper / valueStepper are not overridden to use XTableStepper because that stepper
4647
// would not return the elements in insertion order
48+
4749
private[collection] type Entry = LinkedHashMap.LinkedEntry[K, V]
50+
4851
private[collection] def _firstEntry: Entry = firstEntry
4952

5053
@transient protected var firstEntry: Entry = null
5154

5255
@transient protected var lastEntry: Entry = null
5356

54-
@transient private[this] var table = new Array[Entry](tableSizeFor(LinkedHashMap.defaultinitialSize))
57+
/* Uses the same implementation as mutable.HashMap. The hashtable holds the following invariant:
58+
* - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i.
59+
* - Every bucket is sorted in ascendant hash order
60+
* - The sum of the lengths of all buckets is equal to contentSize.
61+
*/
62+
@transient private[this] var table = new Array[Entry](tableSizeFor(LinkedHashMap.defaultinitialSize))
5563

5664
private[this] var threshold: Int = newThreshold(table.length)
57-
private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashMap.defaultLoadFactor).toInt
5865

5966
private[this] var contentSize = 0
6067

@@ -77,6 +84,7 @@ class LinkedHashMap[K, V]
7784
override def size = contentSize
7885
override def knownSize: Int = size
7986
override def isEmpty: Boolean = size == 0
87+
8088
def get(key: K): Option[V] = {
8189
val e = findEntry(key)
8290
if (e == null) None
@@ -90,23 +98,16 @@ class LinkedHashMap[K, V]
9098
super.contains(key) // A subclass might override `get`, use the default implementation `contains`.
9199
}
92100

93-
override def put(key: K, value: V): Option[V] = {
94-
put0(key, value, true) match {
101+
override def put(key: K, value: V): Option[V] = put0(key, value, true) match {
95102
case null => None
96103
case sm => sm
97104
}
98-
}
99-
100-
override def update(key: K, value: V): Unit = {
101-
put0(key, value, false)
102105

103-
}
106+
override def update(key: K, value: V): Unit = put0(key, value, false)
104107

105-
override def remove(key: K): Option[V] = {
106-
removeEntry0(key) match {
108+
override def remove(key: K): Option[V] = removeEntry0(key) match {
107109
case null => None
108110
case nd => Some(nd.value)
109-
}
110111
}
111112

112113
private[this] def removeEntry0(elem: K): Entry = removeEntry0(elem, computeHash(elem))
@@ -155,17 +156,23 @@ class LinkedHashMap[K, V]
155156

156157
@`inline` private[this] def index(hash: Int) = hash & (table.length - 1)
157158

158-
@`inline` private[this] def findEntry(key: K): Entry = {
159+
@`inline` private[this] def findEntry(key: K): Entry = {
159160
val hash = computeHash(key)
160161
table(index(hash)) match {
161162
case null => null
162163
case nd => nd.findEntry(key, hash)
163164
}
164165
}
165166

166-
def addOne(kv: (K, V)): this.type = { put(kv._1, kv._2); this }
167+
def addOne(kv: (K, V)): this.type = {
168+
put(kv._1, kv._2)
169+
this
170+
}
167171

168-
def subtractOne(key: K): this.type = { remove(key); this }
172+
def subtractOne(key: K): this.type = {
173+
remove(key)
174+
this
175+
}
169176

170177
def iterator: Iterator[(K, V)] = new AbstractIterator[(K, V)] {
171178
private[this] var cur = firstEntry
@@ -195,8 +202,8 @@ class LinkedHashMap[K, V]
195202
val hash = computeHash(key)
196203
val indexedHash = index(hash)
197204

198-
var foundEntry = null.asInstanceOf[Entry]
199-
var previousEntry = null.asInstanceOf[Entry]
205+
var foundEntry: Entry = null
206+
var previousEntry: Entry = null
200207
table(indexedHash) match {
201208
case null =>
202209
case nd =>
@@ -273,36 +280,39 @@ class LinkedHashMap[K, V]
273280
lastEntry = null
274281
}
275282

276-
private[this] def tableSizeFor(capacity: Int) =
277-
(Integer.highestOneBit((capacity-1).max(4))*2).min(1 << 30)
283+
private[this] def tableSizeFor(capacity: Int) =
284+
(Integer.highestOneBit((capacity - 1).max(4)) * 2).min(1 << 30)
285+
286+
private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashMap.defaultLoadFactor).toInt
278287

279288
/*create a new entry. If table is empty(firstEntry is null), then the
280289
* new entry will be the firstEntry. If not, just set the new entry to
281290
* be the lastEntry.
282291
* */
283-
private[this] def createNewEntry(key: K, hash: Int, value: V): Entry =
284-
{
285-
val e = new Entry(key, hash, value)
286-
if (firstEntry eq null) firstEntry = e
287-
else { lastEntry.later = e; e.earlier = lastEntry }
288-
lastEntry = e
289-
e
292+
private[this] def createNewEntry(key: K, hash: Int, value: V): Entry = {
293+
val e = new Entry(key, hash, value)
294+
if (firstEntry eq null) firstEntry = e
295+
else {
296+
lastEntry.later = e
297+
e.earlier = lastEntry
290298
}
299+
lastEntry = e
300+
e
301+
}
291302

292-
/*delete the entry from the linkedhashmap. set its earlier entry's later entry
293-
* and later entry's earlier entry correctly.and set its earlier and later to
294-
* be null.*/
303+
/** Delete the entry from the LinkedHashMap, set the `earlier` and `later` pointers correctly */
295304
private[this] def deleteEntry(e: Entry): Unit = {
296305
if (e.earlier eq null) firstEntry = e.later
297306
else e.earlier.later = e.later
298307
if (e.later eq null) lastEntry = e.earlier
299308
else e.later.earlier = e.earlier
300-
e.earlier = null // Null references to prevent nepotism
309+
e.earlier = null
301310
e.later = null
311+
e.next = null
302312
}
303313

304314
private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] = {
305-
if(contentSize + 1 >= threshold) growTable(table.length * 2)
315+
if (contentSize + 1 >= threshold) growTable(table.length * 2)
306316
val hash = computeHash(key)
307317
val idx = index(hash)
308318
put0(key, value, getOld, hash, idx)
@@ -311,29 +321,27 @@ class LinkedHashMap[K, V]
311321
private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] = {
312322
table(idx) match {
313323
case null =>
314-
val nnode = createNewEntry(key, hash, value)
315-
nnode.next = null
316-
table(idx) = nnode
324+
table(idx) = createNewEntry(key, hash, value)
317325
case old =>
318-
var prev = null.asInstanceOf[Entry]
326+
var prev: Entry = null
319327
var n = old
320-
while((n ne null) && n.hash <= hash) {
321-
if(n.hash == hash && key == n.key) {
328+
while ((n ne null) && n.hash <= hash) {
329+
if (n.hash == hash && key == n.key) {
322330
val old = n.value
323331
n.value = value
324-
return if(getOld) Some(old) else null
332+
return if (getOld) Some(old) else null
325333
}
326334
prev = n
327335
n = n.next
328336
}
329337
val nnode = createNewEntry(key, hash, value)
330-
if(prev eq null) {
331-
table(idx) = nnode
338+
if (prev eq null) {
332339
nnode.next = old
333-
}
334-
else {
340+
table(idx) = nnode
341+
} else {
335342
nnode.next = prev.next
336-
prev.next = nnode}
343+
prev.next = nnode
344+
}
337345
}
338346
contentSize += 1
339347
null
@@ -416,6 +424,7 @@ class LinkedHashMap[K, V]
416424
index += 1
417425
}
418426
}
427+
419428
private def readObject(in: java.io.ObjectInputStream): Unit = {
420429
in.defaultReadObject()
421430
serializeFrom(in, (in.readObject().asInstanceOf[K], in.readObject().asInstanceOf[V]))
@@ -444,8 +453,7 @@ object LinkedHashMap extends MapFactory[LinkedHashMap] {
444453

445454
/** Class for the linked hash map entry, used internally.
446455
*/
447-
private[mutable] final class LinkedEntry[K, V](val key: K, val hash: Int, var value: V)
448-
{
456+
private[mutable] final class LinkedEntry[K, V](val key: K, val hash: Int, var value: V) {
449457
var earlier: LinkedEntry[K, V] = null
450458
var later: LinkedEntry[K, V] = null
451459
var next: LinkedEntry[K, V] = null
@@ -455,22 +463,11 @@ object LinkedHashMap extends MapFactory[LinkedHashMap] {
455463
if (h == hash && k == key) this
456464
else if ((next eq null) || (hash > h)) null
457465
else next.findEntry(k, h)
458-
459-
@tailrec
460-
final def foreach[U](f: ((K, V)) => U): Unit = {
461-
f((key, value))
462-
if (next ne null) next.foreach(f)
463-
}
464-
465-
@tailrec
466-
final def foreachEntry[U](f: (K, V) => U): Unit = {
467-
f(key, value)
468-
if (next ne null) next.foreachEntry(f)
469-
}
470-
471466
}
472467

473-
private[collection] final def defaultLoadFactor: Double = 0.75 // corresponds to 75%
468+
/** The default load factor for the hash table */
469+
private[collection] final def defaultLoadFactor: Double = 0.75
470+
474471
/** The default initial capacity for the hash table */
475472
private[collection] final def defaultinitialSize: Int = 16
476473
}

0 commit comments

Comments
 (0)