【问题标题】:Performance of arrays as alternative to switch cases阵列的性能作为 switch case 的替代方案
【发布时间】:2021-12-07 09:12:00
【问题描述】:

通常,在我在学校遇到的问题中,有些问题希望我们使用 switch case。尽管为了更短的代码,我总是使用数组作为此类问题的替代方案。

因此,使用 Switch-Statement 的解决方案将如下所示:

String day;
int dayNum = n%7; //n is an input

switch(dayNum){

    case 0:
        day = "Sunday";
        break;
    case 1:
        day = "Monday";
        break;
    case 2:
        day = "Tuesday";
        break;
    case 3:
        day = "Wednesday";
        break;
    case 4:
        day = "Thursday";
        break;
    case 5:
        day = "Friday";
        break;
    case 6:
        day = "Saturday";
        break;
}

System.out.println("Today is " + day);

使用数组作为替代方案的解决方案如下所示:

int dayNum = n%7; //n is an input
String[] day = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

System.out.println("Today is " + day[dayNum]);

第二个例子是我通常喜欢使用的那种代码。

我想问的是,这两种解决方案在内存和时间复杂度方面的比较如何?

【问题讨论】:

  • 第一个具有恒定的时间和空间,因为它是在编译时构建的跳转表;第二个具有线性时间和空间,因为它每次都在构造数组。您可以通过构造一次来避免数组构造,并将其存储在一些不可变的类型中(例如,具有不可修改值的 List<String>)。但在性能方面,它可能不会产生显着差异..
  • 您对切换版本的真正反对意见是什么?只是需要更多的行吗?
  • @AndyTurner 线性与什么成正比?一周的天数?
  • 正如 Andy 已经说过的,这两种方法之间的性能差异不会那么显着,除非您有 非常 高性能要求,否则我会选择更易于阅读的版本,理解和维护。
  • “我的代码目标是让它们更短。但有人告诉我,并非所有更短的代码在性能方面也更好。” - 是的,更短并不总是等于更快。实际上,当您考虑开发人员性能 时,有时short 会更慢(更难理解,更容易出错)。在这里,“更短”应该被翻译为“不要不必要地重复相似的部分”和“将代码分成更小、更容易理解的块(方法)”。

标签: java arrays switch-statement


【解决方案1】:

你写的代码只是对开关的使用不当。

当这些东西中的一个或两个都成立时,开关是很棒的:

  • 您触发的值不是连续的(即case 384:,然后是case 30491:,等等。简单的示例是为某个命令打开字符串时。
  • 您想要做的是一堆代码(而不是返回单个结果,如此处)。

对于它的价值,您还以相当复杂的方式编写了 case 块。如果你有一个单独的方法,你可以写:

switch (foo) {
  case 0: return "Sunday";
  case 1: return "Monday";
}

另外(Java 14 中的新功能?),你也可以这样写:

String dayName = switch(foo) {
  case 0 -> "Sunday";
  case 1 -> "Monday";
  default -> throw new IllegalArgumentException("Unknown day");
};

最后,这段代码不应该存在。因为java.time.DayOfWeek 已经存在。你彻底重新发明了轮子。

性能

当谈到 7 个元素时,这并不重要。其他因素占主导地位,就这么简单。

作为一般原则(直到数百甚至数千个,见脚注!),基于数组的代码在编写时是线性的原因是完全因为java 不会(目前;未来的 java 更改正在进行中,可能会改变这一点)意识到“制作”带有日期名称的字符串数组的行可以在 JVM 引导的生命周期内执行一次,并且再也不会执行;换句话说,这段代码在底层编译为创建一个具有 7 个插槽的新字符串数组,然后将 7 个常量分配给插槽(这些常量是对字符串的引用, 计算“每个 VM 生命周期一次”)。

如果您将该数组移动到一个静态字段中,那么它们就会消失并且它们都是 O(1) 性能(无论您有 7 天还是 700 万天,它们的“速度”都是相同的),因为它们都只进行一次查找.没有循环。

重要性能脚注:计算机非常复杂,没有像冯诺依曼模型。因此,基于常识预测代码的性能通常会导致您大错特错。因此,需要牢记 3 条重要规则:

  • 以世界各地的 Java 程序员倾向于编写代码的方式编写代码,并保持灵活性。 如果出现性能问题,您可能需要更改应用的相关部分。如果您的代码灵活,这会更容易。 你读到的关于性能的大多数经验法则都会使你的代码不太灵活,因此很糟糕! - 此外,JVM 优化代码,它本质上是通过作为一个巨大的模式匹配机器来实现的,检测它知道的模式怎么跑好。制作这种模式匹配优化机器的程序员倾向于编写 Java 代码中常用的模式。因此,惯用的 java 代码在实践中往往很快。

  • 没有人可以只看代码就知道它在现实生活中的表现如何,因为它太复杂了。不要相信我的话; 编写虚拟机的工程师们一直都这么说。如果他们想不通,你就没有任何机会。因此,在考虑让代码性能想法决定如何编写代码之前,需要分析器报告或 JMH 计时结果。参见上面的规则:没有这个,最好、最快的代码就是最干净、最灵活、最容易阅读的代码。

  • 一个例外是算法复杂性(大 O 表示法)。这是一个复杂的主题(通常是大学水平的信息学和非常重的数学),远远超出 SO 答案,但如果你想深入研究,可以找到大量在线资源。但请注意,大 O 因素通常不会在获得大量数据之前不要开始控制。例如,在这种情况下,从技术上讲,您的基于数组的代码是 O(n)(因为您在每次运行时都会在代码中创建数组),而开关是 O(1),但假设这里的 n 是 7,那就是无关的。在您找到数百个项目之前,它可能不会变得相关。

【讨论】:

  • 很好的解释,谢谢!
猜你喜欢
  • 1970-01-01
  • 2010-11-28
  • 1970-01-01
  • 2021-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-19
相关资源
最近更新 更多