当前位置: 首页 > 知识库问答 >
问题:

HashMap与Switch语句性能

祁刚毅
2023-03-14

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

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

switch (int) {
    case 1:
    case 2:
    case 3:
    case 4:
    default:
}

然后,它将使用一个表开关,显然比标准HashMap具有性能优势。但是如果switch语句是稀疏的呢?这是我要比较的两个例子:

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

.

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

什么会提供更多的吞吐量,查找开关还是HashMap?HashMap的开销是否会在早期给查找开关带来优势,但最终会随着案例/条目数量的增加而逐渐减少?

编辑:我使用JMH尝试了一些基准测试,下面是我的结果和使用的代码。https://gist.github.com/mooman219/bebbdc047889c7cfe612正如你们提到的,lookupswitch语句的性能优于哈希表。但我还是想知道为什么。

共有3个答案

狄新立
2023-03-14

基于代码易读性和可运维性。两者成本为O(1),几乎没有区别(尽管开关通常会稍微快一点)。

在这种特殊情况下,映射会更快,因为开关返回一个地址,然后必须转到该地址以识别返回值。(一个罕见的例子)。如果您的交换机只是调用函数,那么映射也会更快。

为了加快速度,我会确保使用数字大小写,并避免通过常量或枚举器(typescript)使用字符串。

(编辑)我通过期望确认:Java的开关在引擎盖下是如何工作的?有开关。

switch语句通常具有更高的性能。它创建一个查找表和转到引用,并从该点开始。但也有例外。

当您使用一个简单的开关(如返回映射)时。get(x)vs.switch(1=

无论如何,它们的执行成本应该非常相似。

使用映射将数据解耦,这样可以获得动态创建“切换”案例的好处。详情如下。

如果您需要处理多个复杂的函数/进程,那么如果您使用map来代替,可能会更容易读取/写入。尤其是当开关语句开始超过20或30个选项时。

地图的个人使用案例示例

一段时间以来,我一直在React应用程序中使用以下通量模式(Redux/useReducer)。

我创建了一个中心映射,将触发器映射为键,该值是一个函数引用。然后我可以在有意义的地方和时间加载案例。

最初,我使用它来分解用例以减少文件大小并以更有条理的方式将类似功能的用例组合在一起。尽管后来我将其发展为加载在域中并在域挂钩中配置事件和数据,例如useUser、useFeedback、useProfile等...

这样做允许我将默认状态、初始化函数、事件等创建到逻辑文件结构中,还允许我在需要之前保持较低的占用空间。

有一个注意事项要记住

使用映射不允许直接访问,尽管大多数人认为这段代码有异味。同时,它可以防止意外跌落。

乌骏
2023-03-14

这取决于:

>

如果有很多项目或者您想在不修改太多代码的情况下添加未来的项目---

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

司徒焕
2023-03-14

这里公认的答案是错误的。

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

开关的速度始终与哈希映射的速度一样快。Switch语句转换为直接查找表。对于整数值(int、enum、short、long),它是对语句的直接查找/jmp。不需要进行额外的哈希运算。对于字符串,它预计算case语句的字符串哈希,并使用输入字符串的哈希代码来确定跳转的位置。在发生碰撞的情况下,它会执行if/else链。现在你可能会想“这和HashMap一样,对吧?”但事实并非如此。查找的哈希代码是在编译时计算的,不会根据元素的数量减少(冲突的可能性较低)。

开关具有O(1)查找,而不是O(n)。(好的,事实上,对于一小部分项,开关被转换为if/else语句。这提供了更好的代码位置,避免了额外的内存查找。然而,对于许多项,开关被更改为上面提到的查找表)。

你可以在这里读到更多关于它的信息Java的开关是如何在引擎盖下工作的?

 类似资料:
  • switch 语句可以替代多个 if 判断。 switch 语句为多分支选择的情况提供了一个更具描述性的方式。 语法 switch 语句有至少一个 case 代码块和一个可选的 default 代码块。 就像这样: switch(x) { case 'value1': // if (x === 'value1') ... [break] case 'value2':

  •  使用 switch 语句可以更简洁地实现 if ~ else if 的结构。格式如下。 switch(base_expression) { casecondition_expression1: casecondition_expression2: : : default: : : }  写在 base_expression 位置的表达式会在刚开始时被求值。switch 后面的语句块中的 case

  • C# 中的 switch 语句有些类似于《 if else if 语句》,都可以根据表达式执行某个的语句块,其语法格式如下: switch(表达式){     case value1:     //表达式的值为 value1 时,要执行的代码         break;     case value2:     //表达式的值为 value2 时,要执行的代码         break;   

  • 当条件判断分支太多的时候,我们会使用switch语句来优化逻辑。 package main import "fmt" import "time" func main() { // 基础的switch用法 i := 2 fmt.Print("write ", i, " as ") switch i { case 1: fmt.Println("

  • Swift 条件语句 switch 语句允许测试一个变量等于多个值时的情况。 Swift 语言中只要匹配到 case 语句,则整个 switch 语句执行完成。 语法 Swift 语言中 switch 语句的语法: switch expression { case expression1 : statement(s) fallthrough /* 可选 */

  • switch 语句评估一个表达式,将表达式的值与case子句匹配,并执行与该情况相关联的语句。—— MDN switch 是另一种控制流程的方式,根据条件执行不同的代码块。 能用 switch 实现的都可以用 if 实现。 1. 基本语法 switch (表达式) { case 表达式结果为值1的时候: 做的事情; break; case 表达式结果为值