Dans Scala Map
(voir API ) quelle est la différence de sémantique et de performances entre mapValues
et transform
?
Pour une carte donnée, par exemple
val m = Map( "a" -> 2, "b" -> 3 )
tous les deux
m.mapValues(_ * 5)
m.transform( (k,v) => v * 5 )
fournir le même résultat.
Disons que nous avons un Map[A,B]
. Pour clarification: je fais toujours référence à un Map
immuable.
mapValues
prend une fonction B => C
, où C
est le nouveau type pour les valeurs.
transform
prend une fonction (A, B) => C
, où ce C
est également le type des valeurs.
Les deux aboutiront donc à un Map[A,C]
.
Cependant, avec la fonction transform
, vous pouvez influencer le résultat des nouvelles valeurs par la valeur de leurs clés.
Par exemple:
val m = Map( "a" -> 2, "b" -> 3 )
m.transform((key, value) => key + value) //Map[String, String](a -> a2, b -> b3)
Faire cela avec mapValues
sera assez difficile.
La prochaine différence est que transform
est strict, tandis que mapValues
ne vous donnera qu'une vue, qui ne stockera pas les éléments mis à jour. Cela ressemble à ceci:
protected class MappedValues[C](f: B => C) extends AbstractMap[A, C] with DefaultMap[A, C] {
override def foreach[D](g: ((A, C)) => D): Unit = for ((k, v) <- self) g((k, f(v)))
def iterator = for ((k, v) <- self.iterator) yield (k, f(v))
override def size = self.size
override def contains(key: A) = self.contains(key)
def get(key: A) = self.get(key).map(f)
}
(extrait de https://github.com/scala/scala/blob/v2.11.2/src/library/scala/collection/MapLike.scala#L244 )
Donc, en termes de performances, cela dépend de ce qui est le plus efficace. Si f
est cher et que vous n'accédez qu'à quelques éléments de la carte résultante, mapValues
pourrait être mieux, puisque f
n'est appliqué qu'à la demande. Sinon, je m'en tiendrai à map
ou transform
.
transform
peut également s'exprimer avec map
. Supposons que m: Map[A,B]
Et f: (A,B) => C
, puis
m.transform(f)
est équivalent à m.map{case (a, b) => (a, f(a, b))}
collection.Map
ne fournit pas transform
: il a une signature différente pour les cartes mutables et immuables.
$ scala
Welcome to Scala version 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_11).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val im = Map('a -> 1, 'b -> 2, 'c -> 3)
im: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 1, 'b -> 2, 'c -> 3)
scala> im.mapValues(_ * 7) eq im
res0: Boolean = false
scala> im.transform { case (k,v) => v*7 } eq im
res2: Boolean = false
scala> val mm = collection.mutable.Map('a -> 1, 'b -> 2, 'c -> 3)
mm: scala.collection.mutable.Map[Symbol,Int] = Map('b -> 2, 'a -> 1, 'c -> 3)
scala> mm.mapValues(_ * 7) eq mm
res3: Boolean = false
scala> mm.transform { case (k,v) => v*7 } eq mm
res5: Boolean = true
La transformée mutable mute sur place:
scala> mm.transform { case (k,v) => v*7 }
res6: mm.type = Map('b -> 98, 'a -> 49, 'c -> 147)
scala> mm.transform { case (k,v) => v*7 }
res7: mm.type = Map('b -> 686, 'a -> 343, 'c -> 1029)
La transformation mutable ne change donc pas le type de la carte:
scala> im mapValues (_ => "hi")
res12: scala.collection.immutable.Map[Symbol,String] = Map('a -> hi, 'b -> hi, 'c -> hi)
scala> mm mapValues (_ => "hi")
res13: scala.collection.Map[Symbol,String] = Map('b -> hi, 'a -> hi, 'c -> hi)
scala> mm.transform { case (k,v) => "hi" }
<console>:9: error: type mismatch;
found : String("hi")
required: Int
mm.transform { case (k,v) => "hi" }
^
scala> im.transform { case (k,v) => "hi" }
res15: scala.collection.immutable.Map[Symbol,String] = Map('a -> hi, 'b -> hi, 'c -> hi)
... comme cela peut se produire lors de la construction d'une nouvelle carte.
Voici quelques différences non mentionnées:
mapValues
crée une carte qui n'est PAS sérialisable, sans aucune indication qu'il ne s'agit que d'une vue (le type est Map[_, _]
, mais essayez simplement d'en envoyer un sur le fil).
Puisque mapValues
n'est qu'une vue, chaque instance contient le vrai Map
- qui pourrait être un autre résultat de mapValues
. Imaginez que vous ayez un acteur avec un état, et chaque mutation de l'état définit le nouvel état comme un mapValues
sur l'état précédent ... à la fin, vous avez des cartes profondément imbriquées avec une copie de chaque état précédent de l'acteur (et, oui, ces deux sont de l'expérience).