Разница между mapValues и преобразованием в Map
В Scala Map
(см. API) в чем разница в семантике и производительности между mapValues
и transform
?
Для любого заданного отображения, например
val m = Map( "a" -> 2, "b" -> 3 )
и
m.mapValues(_ * 5)
m.transform( (k,v) => v * 5 )
доставить тот же результат.
Ответы
Ответ 1
Скажем, имеем a Map[A,B]
. Для пояснения: я всегда имею в виду неизменяемый Map
.
mapValues
принимает функцию B => C
, где C
- новый тип для значений.
transform
принимает функцию (A, B) => C
, где этот C
также является типом значений.
Таким образом, оба результата приведут к Map[A,C]
.
Однако с помощью функции transform
вы можете влиять на результат новых значений на значение их ключей.
Например:
val m = Map( "a" -> 2, "b" -> 3 )
m.transform((key, value) => key + value) //Map[String, String](a -> a2, b -> b3)
Выполнение этого с помощью mapValues
будет довольно сложным.
Следующее отличие состоит в том, что transform
является строгим, тогда как mapValues
предоставит вам только представление, которое не будет хранить обновленные элементы. Это выглядит так:
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)
}
(взято из https://github.com/scala/scala/blob/v2.11.2/src/library/scala/collection/MapLike.scala#L244)
Таким образом, производительность зависит от того, что более эффективно. Если f
стоит дорого, и вы получаете доступ только к нескольким элементам результирующей карты, mapValues
может быть лучше, так как f
применяется только по требованию. В противном случае я бы придерживался Map
или transform
.
transform
также можно выразить с помощью Map
. Предположим, что m: Map[A,B]
и f: (A,B) => C
, тогда
m.transform(f)
эквивалентно m.map{case (a, b) => (a, f(a, b))}
Ответ 2
collection.Map
не предоставляет transform
: у него есть другая подпись для изменяемых и неизменяемых Карт.
$ 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
Мутируемое преобразование мутирует на месте:
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)
Таким образом, изменяемое преобразование не изменяет тип карты:
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)
... как это может произойти при построении новой карты.
Ответ 3
Вот несколько не упомянутых различий:
-
mapValues
создает карту, которая НЕ сериализуема, без каких-либо указаний на то, что это просто вид (тип Map[_, _]
, но просто попробуйте отправить его по проводу).
-
Так как mapValues
- это просто представление, каждый экземпляр содержит реальный Map
- который может быть другим результатом mapValues
. Представьте, что у вас есть актер с некоторым состоянием, и каждая мутация состояния устанавливает новое состояние как mapValues
в предыдущем состоянии... в конце концов у вас есть глубоко вложенные карты с копией каждого предыдущего состояния актера (и, да, оба они взяты из опыта).