使用字符串选项优化if-else / switch-case

这段代码会带来什么修改? 在最后几行中,我应该使用更多的if-else结构,而不是“ if-if-if”

if (action.equals("opt1")) { //something } else { if (action.equals("opt2")) { //something } else { if ((action.equals("opt3")) || (action.equals("opt4"))) { //something } if (action.equals("opt5")) { //something } if (action.equals("opt6")) { //something } } } 

稍后编辑:这是Java。 我认为switch-case结构不适用于Strings。

后来编辑2:

交换机使用byte,short,char和int原始数据类型。 它还适用于枚举类型(在类和inheritance中讨论)和一些“包装”某些基本类型的特殊类:Character,Byte,Short和Integer(在简单数据对象中讨论)。

即使你不使用switch语句,是的,如果要避免无用的比较,请使用else:如果第一个if被采用,你不希望在这里评估所有其他ifs,因为它们总是假的。 如果最后一个块是如此缩进以至于在没有滚动的情况下看不到它,你也不需要缩进,下面的代码是完全可读的:

 if (action.equals("opt1")) { } else if (action.equals("opt2")) { } else if (action.equals("opt3")) { } else { } 

使用字符串作为键类型的字典,并将*作为值类型委托。 – 从使用字符串中检索方法将需要O(1 +加载)。

在类的构造函数中填充字典。

  • Java不支持委托,因此解决方法可能需要定义一些内部类 – 每种情况一个,并传递内部类的实例而不是方法作为值。

假设您的语言支持切换字符串,请使用switch语句。

 switch(action) { case "opt6": // break; case "opt7": // ... ... ... } 

在Java中有很多方法可以做到这一点,但这里有一个很好的方法。

 enum Option { opt1, opt2, opt3, opt4, opt5, opt6 } ... switch (Option.valueOf(s)) { case opt1: // do opt1 break; case opt2: // do opt2 break; case opt3: case opt4: // do opt3 or opt4 break; ... } 

请注意,如果参数不是枚举的其中一个成员的名称, valueOf(String)将抛出IllegalArgumentException 。 在引擎盖下,valueOf的实现使用静态hashmap将其String参数映射到枚举值。

你可以使用开关。

 switch (action) { case "opt3": case "opt4": doSomething; break; case "opt5": doSomething; break; default: doSomeWork; break; } 

如果您指定语言可能会有所帮助……因为它看起来像C ++,您可以使用switch。

 switch (action) { case "opt1": // something break; case "opt2": // something break; ... } 

如果您想使用if语句,我认为如果您使用“else if”而不使用花括号,您可以稍微提高可读性和性能,如:

 if (action.equals("opt1")) { //something } else if (action.equals("opt2")) { //something } else if ((action.equals("opt3")) || (action.equals("opt4"))) { //something } else if (action.equals("opt5")) { //something } else if (action.equals("opt6")) { //something } 

我认为一些编译器可以优化else if优于else { if 。 无论如何,我希望我能帮忙!

我会把它清理成一系列if / else语句:

 if(action.equals("opt1")) { // something } else if (action.equals("opt2")) { // something } else if (action.equals("opt3")) { // something } etc... 

这取决于你的语言,但它看起来像C,所以你可以尝试一个switch语句:

 switch(action) { case "opt1": // something break; case "opt2": // something break; case "opt3": case "opt4": // something break; case "opt5": // something break; case "opt6": // something break; } 

但是,有时switch语句不能提供足够的清晰度或灵活性(正如Victor在下面提到的那样,在某些语言中不适用于字符串)。 大多数编程语言都会有一种说“别的”的方式,而不是写作

 if (condition1) { ... } else { if (condition2) { ... } else { if (condition3) { ... } else { // This can get very indented very fast } } } 

…有一堆缩进,你可以写这样的东西:

 if (condition1) { ... } else if (condition2) { ... } else if (condition3) { ... } else { ... } 

在C / C ++中我相信C#, else if 。 在Python中,它是elif

建议使用switch语句的答案是要走的路。 switch语句比你现在拥有的ifif...else语句更容易阅读。

简单的比较很快,并且除了一种情况之外,所有代码都不会执行,因此您可以跳过“优化”并选择“可维护性”。

当然,假设action.equals()方法做了一些微不足道的事情,比如== 。 如果action.equals()很昂贵,那么你还有其他问题。

像这样的程序切换通常由多态性更好地处理 – 而不是由字符串表示的动作,表示具有可以专门化的’某事’方法的对象的动作。 如果您发现需要将字符串映射到该选项,请使用Map

如果你想坚持程序代码,真实代码中的选项都是“optX”:

 if ( action.startsWith("opt") && action.length() == 4 ) { switch ( action.charAt(3) ) { case '1': something; break; case '2': something; break; case '3': something; break; ... } } 

在解析器(破坏字符串是问题域的一部分)之类的东西中是可以的,并且应该很快,但不具有内聚性(对象action和行为之间的连接基于其表示的部分而不是对象的任何内在因素)。

实际上,这取决于分支分析。 如果99%的决定都是“opt1”,那么这段代码就已经相当不错了。 如果99%的决定都是“opt6”,那么这段代码就是丑陋的。

如果您经常“opt6”并且很少“opt1”在第一次比较中输入“opt6”,并根据执行数据流中字符串的频率对以下比较进行排序。

如果你有很多选项并且频率相同,你可以对选项进行排序并将它们拆分成二进制树的forms,如下所示:

 if (action < "opt20") { if( action < "opt10" ) { if( action == "opt4" ) {...} else if( action == "opt2" ) {...} else if( action == "opt1" ) {...} else if( action == "opt8" ) {...} } } else { if( action < "opt30 ) { } else { if( action == "opt38" ) {...} else if( action == "opt32" ) {...} } } 

在此示例中,范围拆分将“opt38”和“opt4”所需的比较减少到3.因此,您可以在每个分支中获得log2(n)+1比较。 这对于选项的相同频率是最佳的。

不要二进制吐出到最后,最后使用4-10“正常”否则,如果按选项的频率排序的构造。 二叉树中的最后两个或三个级别不需要太多进展。

概要

至少对这种比较有两种优化。

  1. 二元决策树
  2. 由于选项的频率而订购

二进制决策树由编译器用于大型switch-case结构。 但是编译器对选项的频率一无所知。 因此,如果一个或两个选项比其他选项更频繁,则根据频率的排序可以对开关盒的使用具有性能益处。 在这种情况下,这是一个解决方法:

 if (action == "opt5") // Processing a frequent (99%) option first { } else // Processing less frequent options (>1%) second { switch( action ) { case "opt1": ... case "opt2": ... } } 

警告

在完成分析之前不要优化代码,这是非常必要的。 最好使用switch-case或者 - 如果是直接的,你的代码保持清洁和可读。 如果您已经优化了代码,请在代码中添加一些好的注释,这样每个人都可以理解这种丑陋的代码安静。 一年之后,您将不会知道分析数据,一些评论将非常有用。

如果你发现本机java开关结构太多限制, 请看一下lambdaj Switcher ,它允许通过将它们与一些hamcrest匹配器匹配来声明性地打开任何对象。

请注意,在switch语句的情况下使用字符串是将在下一版本的Java中添加的新function之一。

请参阅项目硬币:交换机中字符串的提议