web-dev-qa-db-fra.com

Différence entre mapValues ​​et transformation dans Map

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.

23
elm

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))}

42
Kigyo

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.

9
som-snytt

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).

7
codingismy11to7