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]
Map
和HashMap
几乎等价,但不完全一样。
Map is trait, and HashMap 是一个 class。尽管在引擎盖下它们 可能 是同一件事 (scala.collection.immutable.HashMap)(稍后会详细介绍)。
使用时
private val currentVars = Map[String, Long]()
你得到一个 Map
实例。在 scala 中,()
是一个糖,在幕后你实际上是在调用 object Map 的 apply()
方法。这相当于:
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
实现。
我是 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]
Map
和HashMap
几乎等价,但不完全一样。
Map is trait, and HashMap 是一个 class。尽管在引擎盖下它们 可能 是同一件事 (scala.collection.immutable.HashMap)(稍后会详细介绍)。
使用时
private val currentVars = Map[String, Long]()
你得到一个 Map
实例。在 scala 中,()
是一个糖,在幕后你实际上是在调用 object Map 的 apply()
方法。这相当于:
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
实现。