Update the modified map with the default value in Scala

Consider the following code, which calculates the frequency of each line in a list and stores the results in a mutable map. This works fine, but I don't understand where the + = method is defined! Is this some kind of weird implicit conversion thing or what? I saw this code somewhere, but it did not include an explanation for + =.

val list = List("a", "b", "a") val counts = new scala.collection.mutable.HashMap[String, Int]().withDefaultValue(0) list.foreach(counts(_) += 1) counts //> res7: scala.collection.mutable.Map[String,Int] = Map(a -> 2, b -> 1) 

Using a map returns Int, but Int does not have + =, and this method updates the map with a new value, so it looks like apply returns a variable integer that the + = method has.

+4
source share
2 answers

This is not an implicit conversion - it is discoloration. Record:

 x += 1 

desugars:

 x = x + 1 

if the class x does not have the += method defined on it.

Similar:

 counts("a") += 1 

desugars:

 counts("a") = counts("a") + 1 

because counts("a") is Int , and Int does not have a specific += method.

On the other hand, the entry:

 x(expression1) = expression2 

desugars to call the update method in Scala:

 x.update(expression1, expression2) 

Each changed Map has an update method - it allows you to set keys on the map.

Thus, the entire expression is desugared for:

 list.foreach(x => counts.update(x, counts(x) + 1)) 

This += should not be confused with the += method on mutable.Map in Scala. This method updates the record on the map if this key already exists, or adds a new key-value pair. It returns the this link, that is, the same map, so you can link += calls. See ScalaDoc or source code .

+8
source

At these times, when you are wondering what happens with the compiler mask in part of your code, scalac -print is your best friend (see this question ).

If you run scalac -print C.scala , where C.scala is

 package test class C { def myMethod() { val counts = new scala.collection.mutable.HashMap[String, Int]().withDefaultValue(0) counts("a") += 1 } } 

You are getting

 package test { class C extends Object { def myMethod(): Unit = { val counts: collection.mutable.Map = new collection.mutable.HashMap().withDefaultValue(scala.Int.box(0)); counts.update("a", scala.Int.box(scala.Int.unbox(counts.apply("a")).+(1))) }; def <init>(): test.C = { C.super.<init>(); () } } 

It came as a surprise to me too, but obviously scalac will transform

 map(key) =<op> rhs 

to

 map.update(key, map.apply(key) <op> rhs) 
+3
source

All Articles