HashMap与Switch语句性能

Joe*_*e C 23 java hashmap switch-statement

HashMap本质上具有O(1)性能,而开关状态可以具有O(1)或O(log(n)),具体取决于编译器是使用tableswitch还是查找开关.

可以理解的是,如果switch语句是这样编写的,

switch (int) {
    case 1:
    case 2:
    case 3:
    case 4:
    default:
}
Run Code Online (Sandbox Code Playgroud)

那么它将使用一个tableswitch,并且显然比标准的HashMap具有性能优势.但是如果switch语句稀疏怎么办?这将是我要比较的两个例子:

HashMap<Integer, String> example = new HashMap<Integer, String>() {{
        put(1, "a");
        put(10, "b");
        put(100, "c");
        put(1000, "d");
}};
Run Code Online (Sandbox Code Playgroud)

.

switch (int) {
    case 1:
        return "a";
    case 10:
        return "b";
    case 100:
        return "c";
    case 1000:
        return "d";
    default:
        return null;
}
Run Code Online (Sandbox Code Playgroud)

什么会提供更多的吞吐量,lookupswitch或HashMap?HashMap的开销是否能够尽早为lookupswitch提供优势,但随着案例/条目数量的增加最终逐渐减少?

编辑:我尝试了一些使用JMH的基准测试,这里是我的结果和使用的代码.https://gist.github.com/mooman219/bebbdc047889c7cfe612 正如你们提到的,lookupswitch语句的表现优于HashTable.我仍然想知道为什么.

Loc*_*Loc 25

这取决于:

  1. 如果有几个项目| 固定物品.如果可以,使用开关(最坏情况为O(n))

  2. 如果有很多项目或者你想在不修改多少代码的情况下添加未来的项目--->使用hash-map(访问时间被视为常量时间)

  3. 对于你的情况.您不应该担心性能,因为不同的执行时间非常短.只关注代码的可读性/可维护性.是否值得优化一个简单的案例以提高几纳秒?

  • 这是不正确的。如果可以切换,请切换。它总是和 hashmap 一样快。对于整数值和枚举值,它被转换成一个内存查找表,它没有散列的成本。对于字符串,它创建 HashMap 并使用它进行切换。由于代码局部性较低,唯一的切换可能比 if/else 块慢。 (2认同)
  • 当人们说“你不应该担心性能”之类的话时,我真的很不喜欢它。是的,在这种情况下它可能是完全有效的,但总的来说,我们的行业目前正在生产*数千*个软件包,其性能令人无法接受(考虑到我们目前使用的硬件)。问题真的不在于人们对性能考虑_太多_,恰恰相反。即使_if_有些人会考虑太多,好吧,当应用于性能优化时,它肯定比“YAGNI”方法要少得多。:/ (2认同)
  • 再也听不到“别担心性能”这样的话了。这就是为什么人们声称 Java 很慢的原因。在当今的应用程序中,每秒处理数十亿(甚至更多)事件并非不现实。特别是对于开发库来说,关心性能非常重要。所以是的,纳秒也很重要。这个问题涉及代码风格,所以请回答这个问题......如果可以的话。 (2认同)
  • @PerLundberg:你看到我说“针对你的情况”了吗?- 这意味着在这种情况下,问题所有者不应该担心性能。我没有说在所有情况下“你都不应该担心性能”。我们始终需要关心性能。我们也需要优化我们的 API 以提高性能,但不是所有单源代码行优化?因为可读性/可维护性也很重要。有时我们需要进行某种权衡。 (2认同)

Cog*_*man 13

接受的答案在这里是错误的.

http://java-performance.info/string-switch-implementation/

交换机总是和哈希映射一样快.Switch语句转换为直接查找表.在Integer值(int,enums,short,long)的情况下,它是语句的直接查找/ jmp.不需要进行额外的散列.在String的情况下,它预先计算case语句的字符串哈希,并使用输入String的哈希码来确定跳转的位置.在碰撞的情况下,它执行if/else链.现在你可能会想"这和HashMap一样,对吧?" 但事实并非如此.查找的哈希码在编译时计算,并且不会根据元素的数量减少(较低的冲突机会).

开关具有O(1)查找,而不是O(n).(好吧,实际上对于少量项目,交换机转换为if/else语句.这提供了更好的代码局部性并避免了额外的内存查找.但是,对于许多项目,交换机被更改为我上面提到的查找表).

你可以在这里阅读更多关于它的信息 Java的交换机是如何工作的?

  • 我有说哪个更快吗?我没那么说。我的答案是,在哪种情况下哪种比较方便,以及为什么我们不需要担心这种情况的性能,因为我们有大量其他用例需要优化性能,而不是这个简单的情况。 (2认同)