Come ha scritto Daniel, parte del problema che si prova a scrivere in uno stile Java con Double deriva dal fatto che Double
in Scala è scala.Double
e non java.lang.Double
. Se si voleva programmare in stile Java si dovrebbe andare secondo le seguenti linee:
//
// 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 contiene un wrapper Scala per ConcurrentMap
s, in modo da poter facilmente evitare il problema di java.lang.Double
vs scala.Double
. Terrò la struttura del programma per un momento.
//
// 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 variante b) di cui sopra v'è né il problema di rappresentare il valore mancante come 0.0d né il problema che java.lang.Double
non gioca bene con operatori e boxe. Ma entrambe le versioni a) eb) sono discutibili riguardo al loro comportamento concorrente. Il codice di Mansoor utilizza uno ConcurrentHashMap
, che ha lo scopo di consentire l'accesso simultaneo alla mappa. Nella versione originale del codice c'è la possibilità che venga perso un aggiornamento della mappa tra il recupero del vecchio value
e l'archiviazione di value + time
. La variante c) sotto cerca di evitare questo problema.
//
// 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)
Grazie per la spiegazione –