Функциональная разница не будет отображаться в этих тестах:
BaseClass bc = new BaseClass();
bc.DoIt();
DerivedClass dc = new DerivedClass();
dc.ShowIt();
В этом примере, вызванная Doit является той, которую вы ожидаете назвать.
In чтобы увидеть разницу, которую вы должны сделать:
BaseClass obj = new DerivedClass();
obj.DoIt();
Вы увидите, если вы запустите этот тест, который в случае 1 (как вы его определили), DoIt()
в BaseClass
(в том случае, если вы определили его), вызывается DoIt()
в DerivedClass
.
Scalaz имеет концепцию полугруппы , которая фиксирует то, что вы хотите здесь сделать, и приводит к, возможно, кратчайшему / чистому решению:
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> val map1 = Map(1 -> 9 , 2 -> 20)
map1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 9, 2 -> 20)
scala> val map2 = Map(1 -> 100, 3 -> 300)
map2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 100, 3 -> 300)
scala> map1 |+| map2
res2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 109, 3 -> 300, 2 -> 20)
В частности, двоичный оператор для Map[K, V]
объединяет ключи карт, складывая оператор полугруппы V
по любым повторяющимся значениям. Стандартная полугруппа для Int
использует оператор сложения, поэтому вы получаете сумму значений для каждого повторяющегося ключа.
Редактирование: немного больше деталей по запросу пользователя482745.
Математически полугруппа представляет собой всего лишь набор значений вместе с оператором, который принимает два значения из этого множества и выдает другое значение из этого набора. Таким образом, целые числа при добавлении являются полугруппой, например: оператор +
объединяет два ints для создания другого int.
Вы также можете определить полугруппу по множеству «всех карт с заданным типом ключа и тип значения ", пока вы можете придумать некоторую операцию, которая объединяет две карты для создания новой, которая представляет собой комбинацию двух входов.
Если на обеих картах нет ключей , это тривиально. Если один и тот же ключ существует на обеих картах, нам нужно объединить два значения, к которым привязана клавиша. Хм, разве мы не просто описали оператор, который объединяет два объекта одного типа? Поэтому в Scalaz полугруппа для Map[K, V]
существует тогда и только тогда, когда существует полугруппа для V
- полугруппа V
используется для объединения значений из двух карт, которые назначены одному и тому же ключу.
Так как Int
является типом значения здесь, «столкновение» на клавише 1
разрешается путем целочисленного добавления двух отображаемых значений (поскольку это то, что делает оператор полугруппы Int), следовательно 100 + 9
. Если бы значения были Strings, столкновение привело бы к конкатенации строк двух отображаемых значений (опять же, потому что это то, что делает оператор полугруппы для String).
(И интересно, поскольку конкатенация строк не является коммутативный, т. е. "a" + "b" != "b" + "a"
- результирующая полугрупповая операция также не является. Поэтому map1 |+| map2
отличается от map2 |+| map1
в случае String, но не в случае Int.)
Самый быстрый и простой способ:
val m1 = Map(1 -> 1.0, 3 -> 3.0, 5 -> 5.2)
val m2 = Map(0 -> 10.0, 3 -> 3.0)
val merged = (m2 foldLeft m1) (
(acc, v) => acc + (v._1 -> (v._2 + acc.getOrElse(v._1, 0.0)))
)
Таким образом, каждый элемент сразу добавляется к карте.
Второй способ ++
:
map1 ++ map2.map { case (k,v) => k -> (v + map1.getOrElse(k,0)) }
В отличие от первого способа. Во втором случае для каждого элемента во второй карте будет создан и объединен новый список с предыдущей картой.
Выражение case
неявно создает новый список с использованием метода unapply
.
map1 ++ ( for ( (k,v) <- map2 ) yield ( k -> ( v + map1.getOrElse(k,0) ) ) )
Вы также можете сделать это с помощью Cats .
import cats.implicits._
val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)
map1 combine map2 // Map(2 -> 20, 1 -> 109, 3 -> 300)
У меня есть небольшая функция для выполнения этой работы, это в моей небольшой библиотеке для некоторых часто используемых функций, которые не входят в стандартную библиотеку. Он должен работать для всех типов карт, изменяемых и неизменных, а не только для HashMaps
. Вот использование
scala> import com.daodecode.scalax.collection.extensions._
scala> val merged = Map("1" -> 1, "2" -> 2).mergedWith(Map("1" -> 1, "2" -> 2))(_ + _)
merged: scala.collection.immutable.Map[String,Int] = Map(1 -> 2, 2 -> 4)
https://github.com/jozic/ scalax-collection / blob / master / README.md # mergedwith
И вот тело
def mergedWith(another: Map[K, V])(f: (V, V) => V): Repr =
if (another.isEmpty) mapLike.asInstanceOf[Repr]
else {
val mapBuilder = new mutable.MapBuilder[K, V, Repr](mapLike.asInstanceOf[Repr])
another.foreach { case (k, v) =>
mapLike.get(k) match {
case Some(ev) => mapBuilder += k -> f(ev, v)
case _ => mapBuilder += k -> v
}
}
mapBuilder.result()
}
Это может быть реализовано как Monoid с простой Scala. Вот пример реализации. С помощью этого подхода мы можем объединить не только 2, но и список карт.
// Monoid trait
trait Monoid[M] {
def zero: M
def op(a: M, b: M): M
}
Реализация на основе карты моноидного признака, объединяющего две карты.
val mapMonoid = new Monoid[Map[Int, Int]] {
override def zero: Map[Int, Int] = Map()
override def op(a: Map[Int, Int], b: Map[Int, Int]): Map[Int, Int] =
(a.keySet ++ b.keySet) map { k =>
(k, a.getOrElse(k, 0) + b.getOrElse(k, 0))
} toMap
}
Теперь, если у вас есть список карт, которые необходимо объединить (в этом случае всего 2), это можно сделать, как показано ниже.
val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)
val maps = List(map1, map2) // The list can have more maps.
val merged = maps.foldLeft(mapMonoid.zero)(mapMonoid.op)
Это то, что я придумал ...
def mergeMap(m1: Map[Char, Int], m2: Map[Char, Int]): Map[Char, Int] = {
var map : Map[Char, Int] = Map[Char, Int]() ++ m1
for(p <- m2) {
map = map + (p._1 -> (p._2 + map.getOrElse(p._1,0)))
}
map
}
Ну, теперь в библиотеке scala (по крайней мере, в 2.10) есть что-то, что вы хотели - объединенная функция. НО он представлен только в HashMap, а не в Map. Это несколько запутанно. Также подпись громоздка - не могу представить, почему мне нужен ключ дважды, и когда мне нужно будет создать пару с другим ключом. Но, тем не менее, он работает и намного чище, чем предыдущие «родные» решения.
val map1 = collection.immutable.HashMap(1 -> 11 , 2 -> 12)
val map2 = collection.immutable.HashMap(1 -> 11 , 2 -> 12)
map1.merged(map2)({ case ((k,v1),(_,v2)) => (k,v1+v2) })
Также в scaladoc упоминалось, что
Метод
blockquote>merged
в среднем более эффективен чем совершать обход и восстанавливать новую неизменяемую хэш-карту с нуля или++
.
MergeFunction
.
– Mikhail Golubtsov
8 July 2015 в 06:26
private type MergeFunction[A1, B1] = ((A1, B1), (A1, B1)) => (A1, B1)
– EthanP
10 March 2016 в 21:00
Быстрое решение:
(map1.keySet ++ map2.keySet).map {i=> (i,map1.getOrElse(i,0) + map2.getOrElse(i,0))}.toMap
Я написал сообщение в блоге об этом, проверьте:
http://www.nimrodstech.com/scala-map-merge/
, в основном используя полугруппу scalaz, вы можете достичь этого довольно легко.
будет выглядеть примерно так:
import scalaz.Scalaz._
map1 |+| map2
Самый короткий ответ, который я знаю об этом, использует только стандартную библиотеку
map1 ++ map2.map{ case (k,v) => k -> (v + map1.getOrElse(k,0)) }
for
map1 ++ (for ((k, v) & lt; - map2), получаем k - & gt; (v + map1. getOrElse (к, 0)))
– dividebyzero
13 December 2014 в 02:41
Ответ Анджей Дойла содержит большое объяснение полугрупп, которое позволяет вам использовать оператор |+|
для объединения двух карт и суммировать значения для совпадающих ключей.
Существует много способов определить что-то быть экземпляром typeclass, и в отличие от OP вы можете не захотеть точно суммировать свои ключи. Или, возможно, вы захотите работать на объединении, а не на перекрестке. Scalaz также добавляет дополнительные функции для Map
для этой цели:
Вы можете сделать
import scalaz.Scalaz._
map1 |+| map2 // As per other answers
map1.intersectWith(map2)(_ + _) // Do things other than sum the values
scalaz
имеет смысл. – soc 16 August 2011 в 12:34A
иOption[A]
), настолько велика, я не мог поверить, что они были действительно одного и того же типа. I просто i> начал смотреть на Скалаз. Я не уверен, что я достаточно умен ... – Malvolio 17 August 2011 в 01:39