担心我的网络应用程序的性能,我想知道在性能方面,"if/else"语句和switch语句哪个更好?
担心我的网络应用程序的性能,我想知道在性能方面,"if/else"语句和switch语句哪个更好?
我完全同意这个观点,即避免过早地进行优化。
但事实上,Java虚拟机有一些特殊的字节码可用于switch语句。
请参见lookupswitch和tableswitch,详见Java虚拟机规范。
因此,如果代码是性能CPU图的一部分,就可能会有一些性能提升。
这是微观优化和过早优化,它们是有害的。相反,更应该关注所讨论的代码的可读性和可维护性。如果有两个以上的if/else
块粘在一起,或者其大小是不可预测的,那么你可以考虑使用switch
语句。
或者,您还可以使用多态性。首先创建一些接口:
public interface Action {
void execute(String input);
}
可以通过静态或动态的方式获取所有实现,并将它们存储在 Map
中:
Map<String, Action> actions = new HashMap<String, Action>();
最后,将if/else
或switch
替换为以下代码(忽略空指针等细节):
actions.get(name).execute(input);
虽然可能比使用if/else
或switch
微慢一点,但这种代码至少更易于维护。
由于您在谈论Web应用程序,因此可以使用HttpServletRequest#getPathInfo()
作为操作关键字(最终编写一些代码以循环地分离pathinfo的最后一部分,直到找到一个操作)。您可以在这里找到类似的答案:
如果您担心Java EE Web应用程序的性能问题,则这篇文章也可能对您有所帮助。除了(微)优化原始Java代码之外,还有其他领域可以提供更大的性能提升。
如果你遇到性能问题,if/else或switch语句很少会是问题的来源。你应该首先进行性能分析以确定存在哪些缓慢的地方。过早优化是万恶之源!
尽管如此,通过Java编译器优化,我们可以讨论switch与if/else的相对性能。首先请注意,在Java中,switch语句仅适用于非常有限的域 - 整数。一般来说,你可以将switch语句视为以下方式:
switch (<condition>) {
case c_0: ...
case c_1: ...
...
case c_n: ...
default: ...
}
其中c_0
, c_1
, ..., 和 c_N
是 switch 语句的目标整数,<condition>
必须解析为整数表达式。
如果这个集合是"密集的"--也就是说,(max(ci) + 1 - min(ci)) / n > α, 其中 0 < k < α < 1, 其中 k
大于某个经验值,则可以生成跳转表,这是非常高效的。
如果这个集合不是很密集,但是 n >= β,那么二叉搜索树可以在 O(2 * log(n)) 的时间内找到目标,这仍然是有效的。
对于所有其他情况,switch 语句与等价的 if/else 语句序列一样有效率。α 和 β 的精确值取决于多个因素,并由编译器的代码优化模块确定。
最后,当<condition>
的定义域不是整数时,switch 语句完全无用。
使用 switch!
我讨厌维护 if-else 块!这里有一个测试:
public class SpeedTestSwitch
{
private static void do1(int loop)
{
int temp = 0;
for (; loop > 0; --loop)
{
int r = (int) (Math.random() * 10);
switch (r)
{
case 0:
temp = 9;
break;
case 1:
temp = 8;
break;
case 2:
temp = 7;
break;
case 3:
temp = 6;
break;
case 4:
temp = 5;
break;
case 5:
temp = 4;
break;
case 6:
temp = 3;
break;
case 7:
temp = 2;
break;
case 8:
temp = 1;
break;
case 9:
temp = 0;
break;
}
}
System.out.println("ignore: " + temp);
}
private static void do2(int loop)
{
int temp = 0;
for (; loop > 0; --loop)
{
int r = (int) (Math.random() * 10);
if (r == 0)
temp = 9;
else
if (r == 1)
temp = 8;
else
if (r == 2)
temp = 7;
else
if (r == 3)
temp = 6;
else
if (r == 4)
temp = 5;
else
if (r == 5)
temp = 4;
else
if (r == 6)
temp = 3;
else
if (r == 7)
temp = 2;
else
if (r == 8)
temp = 1;
else
if (r == 9)
temp = 0;
}
System.out.println("ignore: " + temp);
}
public static void main(String[] args)
{
long time;
int loop = 1 * 100 * 1000 * 1000;
System.out.println("warming up...");
do1(loop / 100);
do2(loop / 100);
System.out.println("start");
// run 1
System.out.println("switch:");
time = System.currentTimeMillis();
do1(loop);
System.out.println(" -> time needed: " + (System.currentTimeMillis() - time));
// run 2
System.out.println("if/else:");
time = System.currentTimeMillis();
do2(loop);
System.out.println(" -> time needed: " + (System.currentTimeMillis() - time));
}
}
在Windows7中,我进行的测试结果是:ENUM > MAP > SWITCH > IF/ELSE IF 的性能最佳。
import java.util.HashMap;
import java.util.Map;
public class StringsInSwitch {
public static void main(String[] args) {
String doSomething = null;
//METHOD_1 : SWITCH
long start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "Hello World" + (i & 0xF);
switch (input) {
case "Hello World0":
doSomething = "Hello World0";
break;
case "Hello World1":
doSomething = "Hello World0";
break;
case "Hello World2":
doSomething = "Hello World0";
break;
case "Hello World3":
doSomething = "Hello World0";
break;
case "Hello World4":
doSomething = "Hello World0";
break;
case "Hello World5":
doSomething = "Hello World0";
break;
case "Hello World6":
doSomething = "Hello World0";
break;
case "Hello World7":
doSomething = "Hello World0";
break;
case "Hello World8":
doSomething = "Hello World0";
break;
case "Hello World9":
doSomething = "Hello World0";
break;
case "Hello World10":
doSomething = "Hello World0";
break;
case "Hello World11":
doSomething = "Hello World0";
break;
case "Hello World12":
doSomething = "Hello World0";
break;
case "Hello World13":
doSomething = "Hello World0";
break;
case "Hello World14":
doSomething = "Hello World0";
break;
case "Hello World15":
doSomething = "Hello World0";
break;
}
}
System.out.println("Time taken for String in Switch :"+ (System.currentTimeMillis() - start));
//METHOD_2 : IF/ELSE IF
start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "Hello World" + (i & 0xF);
if(input.equals("Hello World0")){
doSomething = "Hello World0";
} else if(input.equals("Hello World1")){
doSomething = "Hello World0";
} else if(input.equals("Hello World2")){
doSomething = "Hello World0";
} else if(input.equals("Hello World3")){
doSomething = "Hello World0";
} else if(input.equals("Hello World4")){
doSomething = "Hello World0";
} else if(input.equals("Hello World5")){
doSomething = "Hello World0";
} else if(input.equals("Hello World6")){
doSomething = "Hello World0";
} else if(input.equals("Hello World7")){
doSomething = "Hello World0";
} else if(input.equals("Hello World8")){
doSomething = "Hello World0";
} else if(input.equals("Hello World9")){
doSomething = "Hello World0";
} else if(input.equals("Hello World10")){
doSomething = "Hello World0";
} else if(input.equals("Hello World11")){
doSomething = "Hello World0";
} else if(input.equals("Hello World12")){
doSomething = "Hello World0";
} else if(input.equals("Hello World13")){
doSomething = "Hello World0";
} else if(input.equals("Hello World14")){
doSomething = "Hello World0";
} else if(input.equals("Hello World15")){
doSomething = "Hello World0";
}
}
System.out.println("Time taken for String in if/else if :"+ (System.currentTimeMillis() - start));
//METHOD_3 : MAP
//Create and build Map
Map<String, ExecutableClass> map = new HashMap<String, ExecutableClass>();
for (int i = 0; i <= 15; i++) {
String input = "Hello World" + (i & 0xF);
map.put(input, new ExecutableClass(){
public void execute(String doSomething){
doSomething = "Hello World0";
}
});
}
//Start test map
start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "Hello World" + (i & 0xF);
map.get(input).execute(doSomething);
}
System.out.println("Time taken for String in Map :"+ (System.currentTimeMillis() - start));
//METHOD_4 : ENUM (This doesn't use muliple string with space.)
start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
String input = "HW" + (i & 0xF);
HelloWorld.valueOf(input).execute(doSomething);
}
System.out.println("Time taken for String in ENUM :"+ (System.currentTimeMillis() - start));
}
}
interface ExecutableClass
{
public void execute(String doSomething);
}
// Enum version
enum HelloWorld {
HW0("Hello World0"), HW1("Hello World1"), HW2("Hello World2"), HW3(
"Hello World3"), HW4("Hello World4"), HW5("Hello World5"), HW6(
"Hello World6"), HW7("Hello World7"), HW8("Hello World8"), HW9(
"Hello World9"), HW10("Hello World10"), HW11("Hello World11"), HW12(
"Hello World12"), HW13("Hello World13"), HW14("Hello World4"), HW15(
"Hello World15");
private String name = null;
private HelloWorld(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void execute(String doSomething){
doSomething = "Hello World0";
}
public static HelloWorld fromString(String input) {
for (HelloWorld hw : HelloWorld.values()) {
if (input.equals(hw.getName())) {
return hw;
}
}
return null;
}
}
//Enum version for betterment on coding format compare to interface ExecutableClass
enum HelloWorld1 {
HW0("Hello World0") {
public void execute(String doSomething){
doSomething = "Hello World0";
}
},
HW1("Hello World1"){
public void execute(String doSomething){
doSomething = "Hello World0";
}
};
private String name = null;
private HelloWorld1(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void execute(String doSomething){
// super call, nothing here
}
}
/*
* https://dev59.com/8XRC5IYBdhLWcg3wUfN2
* https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.10
* http://forums.xkcd.com/viewtopic.php?f=11&t=33524
*/
switch
,其中一些比其他方式更有效。通常情况下,效率不会比直接使用“if”梯形结构差,但由于有足够多的变化(特别是在JITC中),因此很难比这更精确。 - Hot Licksswitch
和if-then-else
块,我想不到有任何值得关注或显著的与性能相关的问题。switch
块,则其使用本身表明您正在从编译时已知的常量集合中获取值进行切换。在这种情况下,如果您可以使用具有特定于常量的方法的enum
,则根本不应该使用switch
语句。switch
语句相比,枚举提供更好的类型安全性和更易于维护的代码。枚举可以设计成如果将常量添加到常量集合中,则必须为新值提供特定于常量的方法才能编译代码。另一方面,忘记向switch
块添加新的case
有时只能在运行时捕获,如果您足够幸运地设置了块以抛出异常。
switch
和enum
特定于常量的方法之间的性能不应有显著差异,但后者更易读、更安全、更易于维护。示例:-> String environment="QA";
switch(environment.toLowerCase().trim()) {
case "qa":
System.out.println(environment+" :"+"Environment running the TestCases");
break;
case "Stage":
System.out.println(environment+" :"+"Environment running the TestCases");
break;
case "Dev":
System.out.println(environment+" :"+"Environment running the TestCases");
break;
case "UAT":
System.out.println(environment+" :"+"Environment running the TestCases");
break;
case "Prod":
System.out.println(environment+" :"+"Environment running the TestCases");
break;
default:
System.out.println(environment+" :"+"Please pass the right Environment");
break;
}
String browser="Chrome";
if (browser.equals("chrome")) {
System.out.println(browser + ": " + " Launch the Browser");
} else if (browser.equals("safari")) {
System.out.println(browser + ": " + " Launch the Browser");
} else if (browser.equals("IE")) {
System.out.println(browser + ": " + " Launch the Browser");
} else if (browser.equals("opera")) {
System.out.println(browser + ": " + " Launch the Browser");
} else if (browser.equals("Edge")) {
System.out.println(browser + ": " + " Launch the Browser");
} else {
System.out.println("Please pass the right browser");
}