为什么这个 flatMap return 是一个 Map 而不是一个列表?

Why does this flatMap return a Map instead of a list?

我正在尝试使用 flatMap 和 map

的组合将下面的 Map[T,List[T]] 转换为 List[T]
val numMap = Map(1 -> List(2) , 3 -> List(2,4))
val numPairs = numMap.flatMap{ case (n, nlist) => nlist.map (x => (x,n) ) }

结果是Map

res12: scala.collection.immutable.Map[Int,Int] = Map(2 -> 3, 4 -> 3)

而不是预期的 List

List((2,1), (2,3), (4,3))

尽管我可以通过一系列 map 操作得到预期的结果

scala> numMap.map{ case (n, nlist) => nlist.map (x => (x,n) ).toList }.flatten
res20: scala.collection.immutable.Iterable[(Int, Int)] = List((2,1), (2,3), (4,3))

想了解为什么flatMap使用失败

I could arrive at the intended result with a sequence of map operations

scala> numMap.map{ case (n, nlist) => nlist.map(x => (x,n)).toList }.flatten
res20: scala.collection.immutable.Iterable[(Int, Int)] = List((2,1), (2,3), (4,3))

与其对所需的结果类型执行额外的转换,不如考虑使用 breakOut:

import scala.collection.breakOut

val numPairs: List[(Int, Int)] =
  numMap.flatMap{ case (n, nlist) => nlist.map((_, n)) }(breakOut)
// numPairs: List[(Int, Int)] = List((2,1), (2,3), (4,3))

使用 numPairs: List[(Int, Int)] 类型注释,breakOut 将结果数据收集到 List[(Int, Int)] 中,同时绕过中间集合的生成。

根据 scala doc 有两个版本的 flatMap 函数:一个 return 是一个新的可迭代集合 另外 return 是一张新地图 。 结果集合的类型由可迭代集合的静态类型决定,这有时可能会导致意想不到的结果。

简单来说,如果我们在 flatMap 参数 return 中提供的函数是单个元素,我们将是 returned 可迭代集合。另一方面,如果 flatMap 参数 return 中的函数是一个集合,我们将 return 编辑一个新的 Map。

例如这是您的输入变量:

scala> val numMap = Map(1 -> List(2) , 3 -> List(2,4))

如果我们 return 只有来自 flatMap 函数参数的单个元素,我们会得到 List

scala> numMap.flatMap{case(k,v) => v}
res0: List(2, 2, 4)

或(return单个元素的另一种方式)

scala> numMap.flatMap{case(k,v) => v.map(e => e)}
res1: List(2, 2, 4)

但是,如果我们 return 从 flatMap 中的函数中收集一个集合,我们将得到 Map。我们在这里 returning 元组 (<element of list>, <key>)

scala> numMap.flatMap{case(k,v) => v.map(e => (e,k))}
res2: Map(2 -> 3, 4 -> 3)

如果我们想要 return 来自 flatMap 函数参数的集合,我们可以使用 case class 并获得 List:

scala> case class MyCollection(a: Int, b: Int)
scala> numMap.flatMap{case(k,v) => v.map(e => MyCollection(e,k))}
res4: List(MyCollection(2,1), MyCollection(2,3), MyCollection(4,3))