加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 为什么这个Iterable在映射后产生一个Set?

发布时间:2020-12-16 18:06:21 所属栏目:安全 来源:网络整理
导读:在下面的示例代码中,为什么Iterable [String] test1在映射后生成一个Set? val foo = Map("a" - 1,"b" - 1)val test1: Iterable[String] = foo.keysval test2: Iterator[String] = foo.keys.toIteratorprintln(test1.map(foo).size) // 1println(test2.map(f
在下面的示例代码中,为什么Iterable [String] test1在映射后生成一个Set?

val foo = Map("a" -> 1,"b" -> 1)
val test1: Iterable[String] = foo.keys
val test2: Iterator[String] = foo.keys.toIterator

println(test1.map(foo).size) // 1
println(test2.map(foo).size) // 2

我对此感到困惑,因为在阅读代码时它完全违反直觉.即使foo.keys只返回一个Iterable,它在调用map时会创建一个Set,因为反射代码显示:

println(test1.map(foo).getClass.getName) // immutable.Set.Set1
println(test2.map(foo).getClass.getName) // Iterator$$anon$11

标准库如何确定它应该在这里创建一个immutable.Set,即使该集合的推断类型只是Iterable [String]?

解决方法

挖掘Kolmar的评论,虽然隐含的参数决定了如何构建结果集合,但在这种情况下,只需查询源集合以供构建器使用.

Iterable.map:

def map[B,That](f: (A) ? B)(implicit bf: CanBuildFrom[Iterable[A],B,That]): That

隐式范围包括与类型args相关的类型,包括Iterable和Int.

Iterable定义了一个“泛型”CanBuildFrom,它在源集合上调用genericBuilder.这就是结果类型与源相关联的方式.

相反,结果集合通过采用CanBuildFrom [From = Nothing,_,_]与源分离.这就是cc.to [Set]的表达方式,其中构建Set而不考虑源集合cc.对于诸如map之类的操作,方法collection.breakOut提供了这样的CanBuildFrom,其中可以有效地推断出结果类型.

您可以为所需行为注入任意CanBuildFrom:

$scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM,Java 1.8.0_92).
Type in expressions for evaluation. Or try :help.

scala> val m = Map("a" -> 1,"b" -> 1)
m: scala.collection.immutable.Map[String,Int] = Map(a -> 1,b -> 1)

scala> val k = m.keys
k: Iterable[String] = Set(a,b)

scala> import collection.{generic,mutable},generic.{CanBuildFrom => CBF},mutable.ListBuffer
import collection.{generic,mutable}
import generic.{CanBuildFrom=>CBF}
import mutable.ListBuffer

scala>   implicit def `as list`: CBF[Iterable[_],Int,List[Int]] =
     |     new CBF[Iterable[_],List[Int]] {
     |       def apply() = new ListBuffer[Int]
     |       def apply(from: Iterable[_]) = apply()
     |     }
as$u0020list: scala.collection.generic.CanBuildFrom[Iterable[_],List[Int]]

scala> k.map(m)
res0: List[Int] = List(1,1)

值得补充的是,完成可以显示2.11.8中的类型:

scala> k.map(m) //print<tab>

$line4.$read.$iw.$iw.k.map[Int,Iterable[Int]]($line3.$read.$iw.$iw.m)(scala.collection.Iterable.canBuildFrom[Int]) // : Iterable[Int]

使用breakOut:

scala> k.map(m)(collection.breakOut)
res1: scala.collection.immutable.IndexedSeq[Int] = Vector(1,1)

scala> k.map(m)(collection.breakOut) //print

$line4.$read.$iw.$iw.k.map[Int,scala.collection.immutable.IndexedSeq[Int]]($line3.$read.$iw.$iw.m)(scala.collection.`package`.breakOut[Any,scala.collection.immutable.IndexedSeq[Int]](scala.Predef.fallbackStringCanBuildFrom[Int])) // : scala.collection.immutable.IndexedSeq[Int]

如图所示,它实际上会为以下操作选择CanBuildFrom:

scala> "abc".map(_ + 1)
res2: scala.collection.immutable.IndexedSeq[Int] = Vector(98,99,100)

scala> "abc".map(_ + 1) //print

scala.Predef.augmentString("abc").map[Int,scala.collection.immutable.IndexedSeq[Int]](((x$1: Char) => x$1.+(1)))(scala.Predef.fallbackStringCanBuildFrom[Int]) // : scala.collection.immutable.IndexedSeq[Int]

相比:

scala> k.map(m)(collection.breakOut) : List[Int] //print

(($line6.$read.$iw.$iw.k.map[Int,List[Int]]($line5.$read.$iw.$iw.m)(scala.collection.`package`.breakOut[Iterable[String],List[Int]](scala.collection.immutable.List.canBuildFrom[Int]))): scala.`package`.List[scala.Int]) // : List[Int]

canonical Q&A on breakOut.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读