Scala 如何知道要使用什么集合实现?

How Scala knows what collection implementation to use?

val set1 = Set(1,2,3)

val list1 = List(1,2,3)

你能描述一下这些物体构造背后的精确机制吗?

Java中我们需要HashSet或者LinkedList来构造对象.

这里我们看到 traits 与 apply 方法一起使用?

这个应用方法在哪里?

隐含的东西?

List 有一个 companion object.

根据 this,有一个伴随对象工厂:

def companion: GenericCompanion[List]

The factory companion object that builds instances of class List.

这里有一些明确使用随播广告的方法:what is the use case for scala List's companion function returning GenericCompanion?

特征可以有伴生对象。因此,当您调用 Set(1, 2, 3) 时,您实际上是在调用伴生对象 Set 上的 apply 方法。例如,您可以查看 Set 伴随对象文档 here, and its source code here

每个集合类型的伴生对象都有一个默认实现,使用 apply 实例化。事实上,集合类型的伴随对象都是从抽象classGenericCompanion扩展而来的,它封装了这种行为。

这是 Scala 不可变集合类型层次结构的图片。从 AB 的箭头表示 B 扩展 A,虚线箭头表示 B 隐含地可视为 A,而粗体行表示 A returns 的伴随对象上的 apply 方法是 B 的实例。

因此,例如,当您调用 Iterable(1, 2, 3) 时,您会得到一个 List,因为从 IterableSeq 到 [=31= 有粗线] 到 List。您可以阅读有关 Scala 集合类型层次结构(包括可变版本)的更多信息 here.

当来自 Java 时,这可能令人惊讶,但实际上很有道理。例如,当您实例化一个 Set 时,您可能不关心实际的实现是什么,实际上只想要 Set 类型契约的所有保证,并且可以相信语言会选择一个执行。

你的问题正好用到了Set,稍微复杂一点。 Sets 少于 5 个元素有特殊的效率实现。因此,上图中的粗箭头仅显示 通常的 情况(对于足够大的 Sets)。例如:

Set(1, 2, 3, 4, 5).asInstanceOf[HashSet[Int]] // Works as expected
Set(1, 2, 3).asInstanceOf[HashSet[Int]] // Doesn't work!
Set(1, 2, 3).asInstanceOf[collection.immutable.Set.Set3[Int]] // Set3 is the special implementation of sets with example 3 elements.

作为关于使用集合的最后一点说明,在此处使用 REPL 对于确定集合特征的默认实现非常有用(无需参考图表)。例如,这里我们可以看到 Iterable 伴生对象构造了一个 List,符合预期:

Iterable(1, 2, 3)
>>> Iterable[Int] = List(1, 2, 3)

List 有一个 companion object,其中 apply 定义为:

override def apply[A](xs: A*): List[A] = xs.toList

更深入

那么,现在我们剩下的问题是 xs 可变参数去哪里了?这基本上归结为 TraversableOnce,它有 toList 调用

  def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = {
    val b = cbf()
    b ++= seq
    b.result()
  }

这导致 CanBuildFrom...这不是一个简单的答案 - 所以我将简单地 point you in the right direction on that one