这是没有意义的:
case t:Map[Int, Map[Int, Y]] forSome { type Y }
由于擦除,您可以检查的是:
case t: Map[_, _]
实际上,这是您不会收到有关擦除的警告的唯一方法。此外,存在类型几乎总是不必要的,而且,就我个人而言,我总是觉得它的语法有点难以正确。_
尽管如此,这是使用来表示存在类型就足够的一种情况。但是请注意,在我自己的代码(第一个版本)中,我不能使用它,因为如果我这样做,类型将不匹配。
下一个,
任意深度的多维数组
这不是一个特别好的主意。你必须知道你将返回什么类型来声明它——如果深度是“任意的”,那么类型也是如此。您可以使用Array[AnyRef]
,但使用起来会很痛苦。
不过,这是一种方法。我取消了所有循环,用 map 替换它们。请注意,我通过调用来利用 aMap
也是 a的事实。另请注意,我只是使用一个数组(使用创建)以避免必须管理地图创建。Function1
map m
map
toArray
def deMap(m: Map[Int, AnyRef]): Array[AnyRef] = {
def translate(x: AnyRef): AnyRef = x match {
case map: Map[Int, AnyRef] => deMap(map)
case s: String => s
case other => throw new IllegalArgumentException("Expected Map or String, got "+other.getClass.toString)
}
m.keys.toArray.sorted map m map translate
}
有两个问题。首先,如果键不连续(Map(0 -> "a", 2 -> "b")
例如 ),则元素将错位。这是一种解决方法,将最后一行代码替换为:
Array.range(0, m.keys.max + 1) map (m getOrElse (_, "")) map translate
在这里,我使用一个空字符串来代表数组中的任何孔。
另一个问题是我假设所有地图都是 type Map[Int, AnyRef]
。为了解决这个问题,可能必须像这样重写:
def deMap(m: Map[Int, AnyRef]): Array[AnyRef] = {
def translate(x: AnyRef): AnyRef = x match {
case map: Map[_, _] =>
val validMap = map collect { case (key: Int, value: AnyRef) => (key -> value) }
deMap(validMap)
case s: String => s
case other => throw new IllegalArgumentException("Expected Map or String, got "+other.getClass.toString)
}
Array.range(0, m.keys.max + 1) map (m getOrElse (_, "")) map translate
}
在这里,我丢弃所有键为 notInt
且值不是的对AnyRef
。可以进一步安全检查代码以测试是否缺少某些内容并在这种情况下报告和错误:
def deMap(m: Map[Int, AnyRef]): Array[AnyRef] = {
def translate(x: AnyRef): AnyRef = x match {
case map: Map[_, _] =>
val validMap = map collect { case (key: Int, value: AnyRef) => (key -> value) }
if (map.size > validMap.size) {
val wrongPairs = map.toSeq diff validMap.toSeq
throw new IllegalArgumentException("Invalid key/value pairs found: "+wrongPairs)
}
deMap(validMap)
case s: String => s
case other => throw new IllegalArgumentException("Expected Map or String, got "+other.getClass.toString)
}
Array.range(0, m.keys.max + 1) map (m getOrElse (_, "")) map translate
}
最后,关于性能。使用map
我所做的方式意味着将为每个map
. 有些人认为这意味着我必须迭代三次而不是一次,但由于每次我执行不同的任务,它并没有真正做更多的工作。然而,我每次分配一个新数组的事实肯定会对性能产生影响——既因为分配惩罚(Java 预初始化所有数组),也因为缓存局部性问题。
避免这种情况的一种方法是使用view
. 当你这样做map
时flatMap
,你会得到filter
一个view
新的视图,并存储该操作以供将来使用(其他方法也可能以这种方式工作——检查文档,或在有疑问时进行测试)。当您最终apply
对view
对象执行操作时,它将应用所有必要的操作来获取您要求的特定元素。每次您针对该元素时它都会这样做apply
,因此性能可能会更好或更差。
这里我先从一个Range
视图开始,避免数组分配,然后Array
在最后将视图转换为an。尽管如此,keys
仍会创建一个集合,从而增加一些开销。在此之后,我将解释如何避免这种情况。
def deMap(m: Map[Int, AnyRef]): Array[AnyRef] = {
def translate(x: AnyRef): AnyRef = x match {
case map: Map[_, _] =>
val validMap = map collect { case (key: Int, value: AnyRef) => (key -> value) }
if (map.size > validMap.size) {
val wrongPairs = map.toSeq diff validMap.toSeq
throw new IllegalArgumentException("Invalid key/value pairs found: "+wrongPairs)
}
deMap(validMap)
case s: String => s
case other => throw new IllegalArgumentException("Expected Map or String, got "+other.getClass.toString)
}
(0 to m.keys.max view) map (m getOrElse (_, "")) map translate toArray
}
但是,您应该留给view
必要的优化,而不是主动使用它们。它不一定比普通集合快,而且它的非严格性可能很棘手。使用的另一种替代方法view
是使用 aStream
代替。AStream
很像 a List
,只是它只按需计算它的元素。这意味着在map
必要之前不会应用任何操作。要使用它,只需将倒数第二行替换为:
Stream.range(0, m.keys.max + 1) map (m getOrElse (_, "")) map translate toArray
Stream
a优于 a的主要优点view
是,一旦计算了 a 中的值Stream
,它就会保持计算状态。这也是它相对于 a 的主要缺点view
,具有讽刺意味的是。在这种特殊情况下,我认为view
更快。
最后,关于先做 amax
而不计算 a Set
(的结果keys
)。AMap
也是一个Iterable
,并且Iterable
都有一个max
方法,所以你可以简单地做m.max
。但是,这将计算 的最大值Tuple2[Int, AnyRef]
,这是一种没有 的类型Ordering
。但是,max
接收一个隐式参数,告诉它Ordering
使用什么,因此可以提供:
m.max(Ordering by ((_: (Int, AnyRef))._1))
这有点拗口,所以我们可以定义我们需要的隐式并使用它,错误,隐式。:-)
def deMap(m: Map[Int, AnyRef]): Array[AnyRef] = {
implicit val myOrdering = Ordering by ((_: (Int, AnyRef))._1)
def translate(x: AnyRef): AnyRef = x match {
case map: Map[_, _] =>
val validMap = map collect { case (key: Int, value: AnyRef) => (key -> value) }
if (map.size > validMap.size) {
val wrongPairs = map.toSeq diff validMap.toSeq
throw new IllegalArgumentException("Invalid key/value pairs found: "+wrongPairs)
}
deMap(validMap)
case s: String => s
case other => throw new IllegalArgumentException("Expected Map or String, got "+other.getClass.toString)
}
(0 to m.max._1 view) map (m getOrElse (_, "")) map translate toArray
}
请注意,它max
返回一个元组key
和value
,因此我们需要_1
获取key
. 另外,请注意,我们Ordering
在每个嵌套的deMap
. 不错,但可以通过在别处定义它来做得更好。
一旦你完成了所有这些,一个while
循环仍然会更快,虽然我不知道多少。如果有足够的 JVM 优化,它们可能会非常接近。另一方面,如果你真的想要速度,你可以一直使用汇编代码。它归结为编写代码的难易程度、维护的难易程度以及运行的快慢之间的平衡。