我听说很多人说flatMap
与map
+ flatten
类似。例如,the answer。
与众不同,对吧?
因为flatMap
将String
视为Char
的序列,所以它将结果的字符串列表展平为一个字符序列(Seq[Char]
)。
flatMap
是map
和flatten
的组合,因此它首先在序列上运行map
,然后运行flatten
给出结果。
但是我今天有一些代码问题。 map
和flatMap
的结果似乎有所不同。这是我的代码
object ListDemo {
def main(args: Array[String]): Unit = {
val map1 = Map("a" -> List(1 ->11,1->111), "b" -> List(2 -> 22, 2 ->222)).map(_._2).flatten
val map2 = Map("a" -> List(1 ->11,1->111), "b" -> List(2 -> 22, 2 ->222)).flatMap(_._2)
map1.foreach(println)
println()
map2.foreach(println)
}
}
结果出乎意料。
(1,11)
(1,111)
(2,22)
(2,222)
(1,111)
(2,222)
为什么会这样?
答案 0 :(得分:7)
在通过.map(f)
在Map
上调用f
时,对于某些K和V(不一定与原始{{ 1}}),结果将为(K, V)
。否则,如果Map
返回某些其他(非对)类型Map[K, V]
,则结果将为f
。因此,如果函数返回一对则为T
,否则返回Iterable[T]
。
在您的情况下,该函数返回Map
,因此结果为Iterable
-而不是Map。 List[(Int, Int)]
然后将其变成Iterable[List[(Int, Int)]]
。
直接使用.flatten
时,您将直接得到Iterable[(Int, Int)]
对,因此结果将是flatMap
-而不是(Int, Int)
。而且由于Map[Int, Int]
不允许重复的键,因此Iterable[(Int, Int)]
包含的元素要少于Map
。
答案 1 :(得分:1)
您在这里有点误会,
因此,假设您有x: M[A]
和f: A = N[B]
的任何Monad
M
和N
,则x.flatMap(f)
应该与{{ 1}}。
但是这里拥有的是嵌套的monad x.map(f).flatten
,而函数是map: M[N[A]]
,具有以下别名,
f: A => B
这种情况与上述将scala> type MapWithStringKey[A] = Map[String, A]
// defined type alias MapWithStringKey
scala> type TupleOfInt = (Int, Int)
// defined type alias TupleOfInt
scala> val map: MapWithStringKey[List[TupleOfInt]] = Map("a" -> List(1 ->11,1->111), "b" -> List(2 -> 22, 2 ->222))
// map: MapWithStringKey[List[TupleOfInt]] = Map(a -> List((1,11), (1,111)), b -> List((2,22), (2,222)))
连接到flatMap
和map
的标准定义完全不同。
现在,这只是非标准情况之一,您可以根据需要选择使用两种选择中的一种。并且,当我们添加flatten
的特殊键唯一性属性(@ sepp2k已在答案中对此进行讨论)时,事情变得更加不可预测。
答案 2 :(得分:1)
TL; DR:两个调用的结果类型不同。对.map().flatten
的调用返回一个Iterable[(Int, Int)]
,对.flatMap()
的调用返回一个Map[Int, Int]
。由于映射可能不会包含两次相同的键,因此每个键的第一项将被第二项覆盖。
将Map
视为Iterable[(Key,Value)]
。调用.map
时,必须为它提供一个返回元组(Key, Value)
的函数(实际类型可能与原始的Key
和Value
不同)。
在您的示例中,Value
恰好是List[(Int, Int)]
。调用.map
并返回原始Value
的{{1}}时,您将得到一个Map
,对Iterable[List[(Int, Int)]]
的调用会变成一个{{ 1}},将“内部”列表串联在一起。如果要将那个转换为地图(通过调用.flatten
),将会看到与Iterable[(Int, Int)]
相同的结果。
现在,.toMap
的不同之处在于它期望返回类型为flatMap
,而不仅仅是flatMap
。然后,它将返回的值用作新构造的Seq[(Key, Value)]
中的条目。
在您的情况下,您的(Key, Value)
的原始Map
满足预期的收益类型,将原始Value
转换为List[(Int, Int)]
。由于映射不能包含具有相同键的两个条目,因此第二次出现该键将替换之前的出现。
要查看此行为,使用REPL(只需运行Map[(String, List[(Int, Int)]
)而不是编写主类将很有帮助,因此您可以看到中间值及其类型。
Map[(Int, Int)]