Skip to content

Commit cd6d03c

Browse files
authored
Merge pull request scala/scala#9074 from retronym/ticket/12047
HashMap bulk operations should retain existing keys
2 parents 9b3ce77 + 09d4dc5 commit cd6d03c

File tree

1 file changed

+71
-49
lines changed

1 file changed

+71
-49
lines changed

library/src/scala/collection/immutable/HashMap.scala

Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ sealed class HashMap[A, +B] extends AbstractMap[A, B]
4646
with CustomParallelizable[(A, B), ParHashMap[A, B]]
4747
with HasForeachEntry[A, B]
4848
{
49-
import HashMap.{nullToEmpty, bufferSize}
49+
import HashMap.{bufferSize, concatMerger, nullToEmpty}
5050

5151
override def size: Int = 0
5252

@@ -159,7 +159,7 @@ sealed class HashMap[A, +B] extends AbstractMap[A, B]
159159
override def values: scala.collection.Iterable[B] = new HashMapValues
160160

161161
override final def transform[W, That](f: (A, B) => W)(implicit bf: CanBuildFrom[HashMap[A, B], (A, W), That]): That =
162-
if ((bf eq Map.canBuildFrom) || (bf eq HashMap.canBuildFrom)) transformImpl(f).asInstanceOf[That]
162+
if ((bf eq Map.canBuildFrom) || (bf eq HashMap.canBuildFrom)) castToThat(transformImpl(f))
163163
else super.transform(f)(bf)
164164

165165
/* `transform` specialized to return a HashMap */
@@ -179,17 +179,18 @@ sealed class HashMap[A, +B] extends AbstractMap[A, B]
179179
override def ++[C >: (A, B), That](that: GenTraversableOnce[C])(implicit bf: CanBuildFrom[HashMap[A, B], C, That]): That = {
180180
if (isCompatibleCBF(bf)) {
181181
//here we know that That =:= HashMap[_, _], or compatible with it
182-
if (this eq that.asInstanceOf[AnyRef]) that.asInstanceOf[That]
183-
else if (that.isEmpty) this.asInstanceOf[That]
184-
else that match {
185-
case thatHash: HashMap[A, B] =>
186-
//default Merge prefers to keep than replace
187-
//so we merge from thatHash
188-
(thatHash.merged(this) (null) ).asInstanceOf[That]
189-
case that =>
190-
var result: HashMap[Any, _] = this.asInstanceOf[HashMap[Any, _]]
191-
that foreach { case kv: (_, _) => result = result + kv }
192-
result.asInstanceOf[That]
182+
if (this eq that.asInstanceOf[AnyRef]) castToThat(that)
183+
else if (that.isEmpty) castToThat(this)
184+
else {
185+
val result: HashMap[A, B] = that match {
186+
case thatHash: HashMap[A, B] =>
187+
this.merge0(thatHash, 0, concatMerger[A, B])
188+
case that =>
189+
var result: HashMap[A, B] = this
190+
that.asInstanceOf[GenTraversableOnce[(A, B)]] foreach { case kv: (_, _) => result = result + kv }
191+
result
192+
}
193+
castToThat(result)
193194
}
194195
} else super.++(that)(bf)
195196
}
@@ -203,42 +204,45 @@ sealed class HashMap[A, +B] extends AbstractMap[A, B]
203204
if (isCompatibleCBF(bf)) addSimple(that)
204205
else super.++:(that)
205206
}
206-
private def addSimple[C >: (A, B), That](that: TraversableOnce[C]): That = {
207+
private def addSimple[C >: (A, B), That](that: TraversableOnce[C])(implicit bf: CanBuildFrom[HashMap[A, B], C, That]): That = {
207208
//here we know that That =:= HashMap[_, _], or compatible with it
208-
if (this eq that.asInstanceOf[AnyRef]) that.asInstanceOf[That]
209-
else if (that.isEmpty) this.asInstanceOf[That]
210-
else that match {
211-
case thatHash: HashMap[A, B] =>
212-
val merger: Merger[A, B] = HashMap.liftMerger[A, B](null)
213-
// merger prefers to keep than replace
214-
// so we invert
215-
(this.merge0(thatHash, 0, merger.invert)).asInstanceOf[That]
216-
217-
case that:HasForeachEntry[A, B] =>
218-
object adder extends Function2[A, B, Unit] {
219-
var result: HashMap[A, B] = this.asInstanceOf[HashMap[A, B]]
220-
val merger = HashMap.liftMerger[A, B](null)
221-
222-
override def apply(key: A, value: B): Unit = {
223-
result = result.updated0(key, computeHash(key), 0, value, null, merger)
209+
if (this eq that.asInstanceOf[AnyRef]) castToThat(that)
210+
else if (that.isEmpty) castToThat(this)
211+
else {
212+
val merger = HashMap.concatMerger[A, B].invert
213+
val result: HashMap[A, B] = that match {
214+
case thatHash: HashMap[A, B] =>
215+
this.merge0(thatHash, 0, HashMap.concatMerger[A, B].invert)
216+
217+
case that:HasForeachEntry[A, B] =>
218+
object adder extends Function2[A, B, Unit] {
219+
var result: HashMap[A, B] = HashMap.this
220+
override def apply(key: A, value: B): Unit = {
221+
result = result.updated0(key, computeHash(key), 0, value, null, merger)
222+
}
224223
}
225-
}
226-
that foreachEntry adder
227-
adder.result.asInstanceOf[That]
228-
case that =>
229-
object adder extends Function1[(A,B), Unit] {
230-
var result: HashMap[A, B] = this.asInstanceOf[HashMap[A, B]]
231-
val merger = HashMap.liftMerger[A, B](null)
232-
233-
override def apply(kv: (A, B)): Unit = {
234-
val key = kv._1
235-
result = result.updated0(key, computeHash(key), 0, kv._2, kv, merger)
224+
that foreachEntry adder
225+
adder.result
226+
case that =>
227+
object adder extends Function1[(A,B), Unit] {
228+
var result: HashMap[A, B] = HashMap.this
229+
override def apply(kv: (A, B)): Unit = {
230+
val key = kv._1
231+
result = result.updated0(key, computeHash(key), 0, kv._2, kv, merger)
232+
}
236233
}
237-
}
238-
that.asInstanceOf[scala.Traversable[(A,B)]] foreach adder
239-
adder.result.asInstanceOf[That]
234+
that.asInstanceOf[scala.Traversable[(A,B)]] foreach adder
235+
adder.result
236+
}
237+
castToThat(result)
240238
}
241239
}
240+
private[this] def castToThat[C, That](m: HashMap[A, B])(implicit bf: CanBuildFrom[HashMap[A, B], C, That]): That = {
241+
m.asInstanceOf[That]
242+
}
243+
private[this] def castToThat[C, That](m: GenTraversableOnce[C])(implicit bf: CanBuildFrom[HashMap[A, B], C, That]): That = {
244+
m.asInstanceOf[That]
245+
}
242246
}
243247

244248
/** $factoryInfo
@@ -261,9 +265,10 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int {
261265
private type MergeFunction[A1, B1] = ((A1, B1), (A1, B1)) => (A1, B1)
262266

263267
private def liftMerger[A1, B1](mergef: MergeFunction[A1, B1]): Merger[A1, B1] =
264-
if (mergef == null) defaultMerger.asInstanceOf[Merger[A1, B1]] else liftMerger0(mergef)
268+
if (mergef == null) defaultMerger[A1, B1] else liftMerger0(mergef)
265269

266-
private val defaultMerger : Merger[Any, Any] = new Merger[Any, Any] {
270+
private def defaultMerger[A, B]: Merger[A, B] = _defaultMerger.asInstanceOf[Merger[A, B]]
271+
private[this] val _defaultMerger : Merger[Any, Any] = new Merger[Any, Any] {
267272
override def apply(a: (Any, Any), b: (Any, Any)): (Any, Any) = a
268273
override def retainIdentical: Boolean = true
269274
override val invert: Merger[Any, Any] = new Merger[Any, Any] {
@@ -273,6 +278,23 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int {
273278
}
274279
}
275280

281+
private def concatMerger[A, B]: Merger[A, B] = _concatMerger.asInstanceOf[Merger[A, B]]
282+
private[this] val _concatMerger : Merger[Any, Any] = new Merger[Any, Any] {
283+
override def apply(a: (Any, Any), b: (Any, Any)): (Any, Any) = {
284+
if (a._1.asInstanceOf[AnyRef] eq b._1.asInstanceOf[AnyRef]) b
285+
else (a._1, b._2)
286+
}
287+
override def retainIdentical: Boolean = true
288+
override val invert: Merger[Any, Any] = new Merger[Any, Any] {
289+
override def apply(a: (Any, Any), b: (Any, Any)): (Any, Any) = {
290+
if (b._1.asInstanceOf[AnyRef] eq a._1.asInstanceOf[AnyRef]) a
291+
else (b._1, a._2)
292+
}
293+
override def retainIdentical: Boolean = true
294+
override def invert = concatMerger
295+
}
296+
}
297+
276298
private[this] def liftMerger0[A1, B1](mergef: MergeFunction[A1, B1]): Merger[A1, B1] = new Merger[A1, B1] {
277299
self =>
278300
def apply(kv1: (A1, B1), kv2: (A1, B1)): (A1, B1) = mergef(kv1, kv2)
@@ -1049,7 +1071,7 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int {
10491071

10501072
/** The root node of the partially build hashmap */
10511073
private var rootNode: HashMap[A, B] = HashMap.empty
1052-
private def plusPlusMerger = liftMerger[A, B](null).invert
1074+
10531075
private def isMutable(hs: HashMap[A, B]) = {
10541076
hs.isInstanceOf[HashTrieMap[A, B]] && hs.size == -1
10551077
}
@@ -1249,11 +1271,11 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int {
12491271
if (toNode eq toBeAdded) toNode
12501272
else toBeAdded match {
12511273
case bLeaf: HashMap1[A, B] =>
1252-
if (toNodeHash == bLeaf.hash) toNode.merge0(bLeaf, level, plusPlusMerger)
1274+
if (toNodeHash == bLeaf.hash) toNode.merge0(bLeaf, level, concatMerger[A, B])
12531275
else makeMutableTrie(toNode, bLeaf, level)
12541276

12551277
case bLeaf: HashMapCollision1[A, B] =>
1256-
if (toNodeHash == bLeaf.hash) toNode.merge0(bLeaf, level, plusPlusMerger)
1278+
if (toNodeHash == bLeaf.hash) toNode.merge0(bLeaf, level, concatMerger[A, B])
12571279
else makeMutableTrie(toNode, bLeaf, level)
12581280

12591281
case bTrie: HashTrieMap[A, B] =>

0 commit comments

Comments
 (0)