担心我的Web应用程序的性能,我想知道“ if / else”或switch语句在性能方面哪个更好?
if
等等
担心我的Web应用程序的性能,我想知道“ if / else”或switch语句在性能方面哪个更好?
if
等等
Answers:
那是微优化和过早的优化,这是邪恶的。相当担心有关代码的可读性和可维护性。如果有两个以上的if/else
块粘合在一起或者其大小不可预测,那么您可能会考虑使用一个switch
声明。
另外,您也可以使用Polymorphism。首先创建一些接口:
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()
as作为操作键(最终编写更多代码以将pathinfo的最后部分循环分离,直到找到操作为止)。您可以在这里找到类似的答案:
如果您总体上担心Java EE Web应用程序的性能,那么您可能会发现本文也很有用。在其他方面,与仅对原始Java代码进行(微)优化相比,可以带来更多的性能提升。
我完全同意避免过早优化的观点。
但是的确,Java VM具有可以用于switch()的特殊字节码。
请参阅WM规范(lookupswitch和tableswitch)
因此,如果代码是性能CPU图形的一部分,则可能会提高性能。
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(c i)+ 1-min(c i))/ n>α,其中0 <k <α<1,其中k
大于某些经验值,则a可以生成跳转表,效率很高。
如果此集合不是很密集,但是n> =β,则二叉搜索树可以在O(2 * log(n))中找到目标,这仍然非常有效。
对于所有其他情况,switch语句的效率与等效的if / else语句系列的效率完全相同。α和β的精确值取决于许多因素,并由编译器的代码优化模块确定。
最后,当然,如果of的域<condition>
不是整数,则switch语句完全没有用。
使用开关!
我讨厌维持if-else-blocks!进行测试:
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));
}
}
switch
es优化了吗?
我记得读过Java字节码中有2种Switch语句。(我认为这是在“ Java Performance Tuning”中,这是一种非常快速的实现,它使用switch语句的整数值来知道要执行的代码的偏移量。这将要求所有整数必须连续且在定义良好的范围内我猜测使用Enum的所有值也将属于该类别。
不过,我也同意其他许多发帖人的观点...担心此为时过早,除非这是非常非常热的代码。
switch
了几种不同的方法,其中一些方法比其他方法更有效。通常,效率不会比简单的“ if
阶梯”差,但是存在足够的变化(尤其是JITC),很难做到比这更精确。
根据Cliff Click在他的2009年Java One讲座“现代硬件崩溃课程”中的说法:
如今,性能主要由内存访问模式决定。缓存未命中占主导地位–内存是新磁盘。[幻灯片65]
你可以在这里找到他的完整幻灯片。
Cliff给出了一个示例(在幻灯片30上完成),该示例显示即使CPU执行寄存器重新命名,分支预测和推测性执行,由于两个高速缓存未命中,这也只能在4个时钟周期内启动7个操作,然后才需要阻塞300个时钟周期返回。
因此,他说,为了加快程序运行速度,您不应该关注这类次要问题,而应该关注较大的问题,例如是否进行不必要的数据格式转换,例如转换“ SOAP→XML→DOM→SQL→…”。 “,”将所有数据通过缓存“。
在我的测试中,更好的性能是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
}
}
/*
* http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string
* 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
*/
Time taken for String in Switch :3235 Time taken for String in if/else if :3143 Time taken for String in Map :4194 Time taken for String in ENUM :2866
对于大多数switch
最if-then-else
块,我不能想象有任何明显的或显著的性能相关的问题。
事情是这样的:如果您使用的是switch
块,那么它的用法暗示您正在打开一个在编译时已知的一组常量中获取的值。在这种情况下,switch
如果可以使用enum
特定于常量的方法,则实际上根本不应该使用语句。
与switch
语句相比,枚举提供了更好的类型安全性和易于维护的代码。可以设计枚举,以便在将常量添加到一组常量时,如果不为新值提供特定于常量的方法,则代码将无法编译。另一方面,有时只有在运行时才有幸忘记添加新case
的switch
块,如果您足够幸运地设置了块以引发异常。
特定switch
于enum
常数的方法之间的性能应该没有显着差异,但是后者更具可读性,更安全且更易于维护。