听起来好像 packed-switch
等同于爪哇 tableswitch
, , 和 sparse-switch
至 lookupswitch
.
一个 packed-switch
使用一个简单的跳台,由表单索引 low + n
, , 在哪里 low
是最低的测试值 case
标签,和 n
是输入 switch
. 。每个索引上的值代表每个intecode偏移 case
. 。找到正确的跳跃地址是一个恒定的时间操作。
一个 sparse-switch
使用键值对的排序列表,其中每个键是来自一个的测试值 case
标签,值是跳跃偏移。找到正确的跳跃目标 lookupswitch
需要在密钥上进行二进制搜索,因此它是对数时间操作。
编译器将选择要使用的。如果键倾向于聚集或 包装 紧密合在一起,然后 packed-switch
(或者,用爪哇术语 tableswitch
)可以有效地发射。但是如果钥匙是 疏, ,以及值的范围(high - low + 1
)很大,然后使用跳台需要大量的字节码,因为该范围内的所有值必须在跳台中都存在,无论是否有相应的 case
标签。在这些情况下,编译器将发出 sparse-switch
(lookupswitch
).
有趣的是,Dalvik工程师选择以描述应使用的关键分布的方式命名这些Opcodes,而Java工程师选择了描述字节码操作数相似的概念数据结构的名称。
让我们看一些例子。考虑以下Java代码,该代码将产生一个 tableswitch
(并且,当转换为Dalvik时 packed-switch
):
static String packedSwitch(final int n) {
switch (n) {
case 5:
return "Five";
case 3:
return "Three";
case 1:
return "One";
default:
return "Other";
}
}
从概念上讲,有效载荷 packed-switch
OpCode看起来像这样:
如您所见,它相当紧凑。五个插槽中有三个指向实际 case
目标,其余两个跳到 default
目标。但是,如果我们的测试值更加分布怎么办?
static String sparseSwitch(final int n) {
switch (n) {
case 500:
return "Five Hundred";
case 300:
return "Three Hundred";
case 100:
return "One Hundred";
default:
return "Other";
}
}
如果编译器试图将其作为一个 packed-switch
, ,有效载荷看起来像这样:
请注意,几百个插槽中只有三个指向 case
来自原始代码的标签。其余的只是为了填写跳台。不是很高效,是吗?这就是为什么编译器会发出 sparse-switch
, ,在此特定示例中具有更紧凑的字节码足迹:
现在,这更合理,您不觉得吗?但是,不利的一面是,我们不必确切地知道要根据输入跳到哪个索引,而是必须在表上执行二进制搜索,直到找到匹配的测试值。开关越大,对性能的影响越大,尽管效果具有对数曲线。