Skip to content

Commit 5f63805

Browse files
committed
Implement @retronym's suggestions
1 parent f02c4ee commit 5f63805

File tree

2 files changed

+41
-88
lines changed

2 files changed

+41
-88
lines changed

library/src/scala/collection/immutable/ChampHashMap.scala

Lines changed: 5 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ private[immutable] final class HashMapBuilder[K, V] extends Builder[(K, V), Hash
935935
}
936936

937937
/** Upserts a key/value pair into mapNode, mutably */
938-
private def update(mapNode: MapNode[K, V], key: K, value: V, originalHash: Int, keyHash: Int, shift: Int): Unit = {
938+
private[immutable] def update(mapNode: MapNode[K, V], key: K, value: V, originalHash: Int, keyHash: Int, shift: Int): Unit = {
939939
mapNode match {
940940
case bm: BitmapIndexedMapNode[K, V] =>
941941
val mask = maskFrom(keyHash, shift)
@@ -984,55 +984,6 @@ private[immutable] final class HashMapBuilder[K, V] extends Builder[(K, V), Hash
984984
}
985985
}
986986

987-
/** Inserts the this key/value only if the key is not present in the map already
988-
* This method is safe to all, even when aliased, since it will only ensure unaliased if it is going to mutate */
989-
private def updateIfNotExists(mapNode: MapNode[K, V], key: K, value: V, originalHash: Int, keyHash: Int, shift: Int): Unit = {
990-
mapNode match {
991-
case bm: BitmapIndexedMapNode[K, V] =>
992-
val mask = maskFrom(keyHash, shift)
993-
val bitpos = bitposFrom(mask)
994-
if ((bm.dataMap & bitpos) != 0) {
995-
val index = indexFrom(bm.dataMap, mask, bitpos)
996-
val key0 = bm.getKey(index)
997-
998-
if (key0 == key) {
999-
() // do nothing
1000-
} else {
1001-
ensureUnaliased()
1002-
1003-
val value0 = bm.getValue(index)
1004-
val key0UnimprovedHash = bm.getHash(index)
1005-
val key0Hash = improve(key0UnimprovedHash)
1006-
1007-
val subNodeNew: MapNode[K, V] =
1008-
bm.mergeTwoKeyValPairs(key0, value0, key0UnimprovedHash, key0Hash, key, value, originalHash, keyHash, shift + BitPartitionSize)
1009-
1010-
hash += keyHash
1011-
migrateFromInlineToNode(bm, bitpos, subNodeNew)
1012-
}
1013-
1014-
} else if ((bm.nodeMap & bitpos) != 0) {
1015-
val index = indexFrom(bm.nodeMap, mask, bitpos)
1016-
val subNode = bm.getNode(index)
1017-
val beforeSize = subNode.size
1018-
updateIfNotExists(subNode, key, value, originalHash, keyHash, shift + BitPartitionSize)
1019-
bm.size += subNode.size - beforeSize
1020-
} else {
1021-
ensureUnaliased()
1022-
insertValue(bm, bitpos, key, originalHash, keyHash, value)
1023-
hash += keyHash
1024-
}
1025-
case hc: HashCollisionMapNode[K, V] =>
1026-
val index = hc.indexOf(key)
1027-
if (index < 0) {
1028-
ensureUnaliased()
1029-
hash += keyHash
1030-
hc.content = hc.content.appended((key, value))
1031-
}
1032-
}
1033-
}
1034-
1035-
1036987
/** If currently referencing aliased structure, copy elements to new mutable structure */
1037988
private def ensureUnaliased() = {
1038989
if (isAliased) copyElems()
@@ -1064,11 +1015,10 @@ private[immutable] final class HashMapBuilder[K, V] extends Builder[(K, V), Hash
10641015
this
10651016
}
10661017

1067-
1068-
private[collection] def addOneIfNotExists(key: K, value: V): this.type = {
1069-
val h = key.##
1070-
val im = improve(h)
1071-
updateIfNotExists(rootNode, key, value, h, im, 0)
1018+
def addOne(key: K, value: V): this.type = {
1019+
ensureUnaliased()
1020+
val originalHash = key.##
1021+
update(rootNode, key, value, originalHash, improve(originalHash), 0)
10721022
this
10731023
}
10741024

library/src/scala/collection/immutable/Map.scala

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -333,15 +333,8 @@ object Map extends MapFactory[Map] {
333333
}
334334
}
335335

336-
final class Map4[K, +V](
337-
private[collection] val key1: K,
338-
private[collection] val value1: V,
339-
private[collection] val key2: K,
340-
private[collection] val value2: V,
341-
private[collection] val key3: K,
342-
private[collection] val value3: V,
343-
private[collection] val key4: K,
344-
private[collection] val value4: V) extends AbstractMap[K, V] with StrictOptimizedIterableOps[(K, V), Iterable, Map[K, V]] {
336+
final class Map4[K, +V](key1: K, value1: V, key2: K, value2: V, key3: K, value3: V, key4: K, value4: V)
337+
extends AbstractMap[K, V] with StrictOptimizedIterableOps[(K, V), Iterable, Map[K, V]] {
345338

346339
override def size: Int = 4
347340
override def knownSize: Int = 4
@@ -406,6 +399,13 @@ object Map extends MapFactory[Map] {
406399
override def foreach[U](f: ((K, V)) => U): Unit = {
407400
f((key1, value1)); f((key2, value2)); f((key3, value3)); f((key4, value4))
408401
}
402+
403+
private[immutable] def buildTo[V1 >: V](builder: HashMapBuilder[K, V1]): Unit = {
404+
builder.addOne(key1, value1)
405+
builder.addOne(key2, value2)
406+
builder.addOne(key3, value3)
407+
builder.addOne(key4, value4)
408+
}
409409
}
410410

411411
// scalac generates a `readReplace` method to discard the deserialized state (see https://github.com/scala/bug/issues/10412).
@@ -418,46 +418,49 @@ object Map extends MapFactory[Map] {
418418
@SerialVersionUID(3L)
419419
abstract class AbstractMap[K, +V] extends scala.collection.AbstractMap[K, V] with Map[K, V]
420420

421-
private[collection] final class MapBuilderImpl[K, V] extends Builder[(K, V), Map[K, V]] {
421+
private[immutable] final class MapBuilderImpl[K, V] extends Builder[(K, V), Map[K, V]] {
422422
private[this] var elems: Map[K, V] = Map.empty
423+
private[this] var switchedToHashMapBuilder: Boolean = false
423424
private[this] var hashMapBuilder: HashMapBuilder[K, V] = _
424425

425426
override def clear(): Unit = {
426427
elems = Map.empty
427428
if (hashMapBuilder != null) {
428429
hashMapBuilder.clear()
429430
}
431+
switchedToHashMapBuilder = false
430432
}
431433

432-
override def result(): Map[K, V] = {
433-
if (hashMapBuilder == null || hashMapBuilder.size == 0) {
434-
elems
435-
} else if (elems.size == 4) {
436-
val map4 = elems.asInstanceOf[Map4[K, V]]
434+
override def result(): Map[K, V] =
435+
if (switchedToHashMapBuilder) hashMapBuilder.result() else elems
437436

438-
hashMapBuilder.addOneIfNotExists(map4.key1, map4.value1)
439-
hashMapBuilder.addOneIfNotExists(map4.key2, map4.value2)
440-
hashMapBuilder.addOneIfNotExists(map4.key3, map4.value3)
441-
hashMapBuilder.addOneIfNotExists(map4.key4, map4.value4)
442-
443-
hashMapBuilder.result()
444-
} else {
445-
// should never happen...
446-
elems.foreach { case (k, v) => hashMapBuilder.addOneIfNotExists(k, v) }
447-
hashMapBuilder.result()
448-
}
449-
}
450-
override def addOne(elem: (K, V)) = {
451-
if (elems.size < 4) {
437+
def addOne(elem: (K, V)) = {
438+
if (switchedToHashMapBuilder) {
439+
hashMapBuilder.addOne(elem)
440+
} else if (elems.size < 4) {
452441
elems = elems + elem
453442
} else {
454-
if (hashMapBuilder == null) {
455-
hashMapBuilder = new HashMapBuilder
443+
// assert(elems.size == 4)
444+
if (elems.contains(elem._1)) {
445+
elems = elems + elem
446+
} else {
447+
switchedToHashMapBuilder = true
448+
if (hashMapBuilder == null) {
449+
hashMapBuilder = new HashMapBuilder
450+
}
451+
elems.asInstanceOf[Map4[K, V]].buildTo(hashMapBuilder)
452+
hashMapBuilder.addOne(elem)
456453
}
457-
458-
hashMapBuilder.addOne(elem)
459454
}
460455

461456
this
462457
}
458+
459+
override def addAll(xs: IterableOnce[(K, V)]): this.type =
460+
if (switchedToHashMapBuilder) {
461+
hashMapBuilder.addAll(xs)
462+
this
463+
} else {
464+
super.addAll(xs)
465+
}
463466
}

0 commit comments

Comments
 (0)