Scala - 多种初始化容器的方式

Scala - Multiple ways of initializing containers

我是 Scala 的新手,想知道使用以下三种方式初始化 Map 数据结构有什么区别:

 private val currentFiles: HashMap[String, Long] = new HashMap[String, Long]()
 private val currentJars = new HashMap[String, Long]
 private val currentVars = Map[String, Long]

MapHashMap几乎等价,但不完全一样。

Map is trait, and HashMap 是一个 class。尽管在引擎盖下它们 可能 是同一件事 (scala.collection.immutable.HashMap)(稍后会详细介绍)。

使用时

private val currentVars = Map[String, Long]()

你得到一个 Map 实例。在 scala 中,() 是一个糖,在幕后你实际上是在调用 object Mapapply() 方法。这相当于:

private val currentVars = Map.apply[String, Long]()

使用

private val currentJars = new HashMap[String, Long]()

你得到一个 HashMap 实例。

在第三个语句中:

private val currentJars: HashMap[String, Long] = new HashMap[String, Long]()

您只是不再依赖 type inference。这与第二个语句完全相同

private val currentJars: HashMap[String, Long] = new HashMap[String, Long]()
private val currentJars = new HashMap[String, Long]() // same thing

什么时候/我用哪个/为什么

关于类型推断,我建议你使用类型推断。恕我直言,在这种情况下,它从代码中删除了并不真正需要的冗长内容。但是如果你真的想念 like-java 代码,那么包括类型 :) .

现在,关于两个构造函数...

Map 与 HashMap

简答

您可能应该始终使用 Map():它更短,已经导入并且 return 是一个特征(如 java 界面)。最后一个原因很好,因为在传递此 Map 时,您不会依赖实现细节,因为 Map 只是您想要或需要的接口。

另一方面,HashMap一个实现。

长答案

Map 并不总是 HashMap.

Programming in Scala 中所示,Map.apply[K, V]() 可以 return 不同的 class 取决于您传递的键值对数量对它 (ref):

Number of elements       Implementation

0                        scala.collection.immutable.EmptyMap

1                        scala.collection.immutable.Map1 

2                        scala.collection.immutable.Map2

3                        scala.collection.immutable.Map3

4                        scala.collection.immutable.Map4 

5 or more                scala.collection.immutable.HashMap

当你的元素少于 5 个时,你会为每个小集合获得一个特殊的 class,当你有一个空 Map 时,你会得到一个单例对象。

这样做主要是为了获得更好的性能。

你可以在repl中试试看:

import scala.collection.immutable.HashMap

val m2 = Map(1 -> 1, 2 -> 2)
m2.isInstanceOf[HashMap[Int, Int]]
// false

val m5 = Map(1 -> 1, 2 -> 2, 3 -> 3, 4 -> 4, 5 -> 5, 6 -> 6)
m5.isInstanceOf[HashMap[Int, Int]]
// true

如果你真的很好奇,你甚至可以看一看source code

因此,即使为了性能,您也应该坚持使用 Map()

你的问题有两个不同的部分。

首先,是否使用显式类型(情况 1 和 2)之间的区别适用于任何 class,不一定是容器。

 val x = 1

这里的类型不是显式的,编译器会尝试使用类型推断来弄清楚。 x 的类型将是 Int.

val x: Int = 1

同上,但现在明确。如果 = 右边的内容无法转换为 Int,则会出现编译器错误。

val x: Any = 1

这里我们仍然会存储一个1,但是变量的类型将是父class,使用多态性。


你问题的第二部分是关于初始化的。基本初始化如 java:

val x = new List[Int]()

这会调用 class 构造函数,并且 return 是 class 的新实例。 现在,有一个名为 .apply 的特殊方法,您可以只用括号定义和调用它,如下所示:

val x = Seq[Int]()

这是一个快捷方式:

val x = Seq.apply[Int]()

注意这是 Seq 对象上的一个函数。 return 类型是函数想要的任何类型,它只是另一个函数。也就是说,它主要用于 return 给定类型的新实例,但没有任何保证,您需要查看函数文档以确保合同。

也就是说,在 val x = Map[String, Long]() 的情况下,实现 return 是 immutable.HashMap[String, Long] 的实际实例,这是一种默认的 Map 实现。