As Daniel wrote, part of the problem trying to write in a Java style with Double comes from the fact that Double
in Scala is scala.Double
and not java.lang.Double
. If you wanted to program in Java style you would have to go along the following lines:
//
// a) Java style, with concurrency problem
//
import java.lang.{Double=>JDouble}
import java.util.concurrent.ConcurrentHashMap
val map = new ConcurrentHashMap[String, JDouble]
def update(name: String, time: Double) {
val value: JDouble = map.get(name)
if (value eq null)
map.put(name, time)
else
map.put(name, JDouble.valueOf(value.doubleValue + time))
}
update("foo", 42.0d)
update("foo", 41.0d)
assert(map.get("foo") == 83.0d)
Scala 2.8 contains a Scala wrapper for ConcurrentMap
s, so you can easily avoid the problem of java.lang.Double
vs scala.Double
. I'll keep the program structure for a moment.
//
// b) Scala style, with concurrency problem.
//
import collection.JavaConversions._
import collection.mutable.ConcurrentMap
import java.util.concurrent.ConcurrentHashMap
val map: ConcurrentMap[String, Double] = new ConcurrentHashMap[String,Double]()
def update(name: String, time: Double) {
map.get(name) match {
case None => map.put(name, time)
case Some(value) => map.put(name, value + time)
}
}
update("foo", 42.0d)
update("foo", 41.0d)
assert(map("foo") == 83.0d)
In variant b) above there is neither the problem of representing the missing value as 0.0d nor the problem that java.lang.Double
doesn't play nicely with operators and boxing. But both versions a) and b) are questionable regarding their concurrency behaviour. Mansoor's code uses a ConcurrentHashMap
, which has the purpose of allowing for concurrent access to the map. In the original version of the code there is a chance that an update to the map is lost between getting the old value
and storing value + time
.
Variant c) below tries to avoid that problem.
//
// c) Scala style, hopefully safe for concurrent use ;)
//
import collection.JavaConversions._
import collection.mutable.ConcurrentMap
import java.util.concurrent.ConcurrentHashMap
val map: ConcurrentMap[String, Double] = new ConcurrentHashMap[String,Double]()
def update(name: String, time: Double) {
val valueOption: Option[Double] = map.putIfAbsent(name, time)
def replace(value: Double) {
val replaced = map.replace(name, value, value + time)
if (!replaced) {
replace(map(name))
}
}
valueOption foreach { replace(_) }
}
update("foo", 42.0d)
update("foo", 41.0d)
assert(map("foo") == 83.0d)