Java 中 if/else 与 switch 语句的相对性能差异是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2086529/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the relative performance difference of if/else versus switch statement in Java?
提问by Anth0
Worrying about my web application's performances, I am wondering which of "if/else" or switch statement is better regarding performance?
担心我的 Web 应用程序的性能,我想知道“if/else”或 switch 语句中哪一个在性能方面更好?
采纳答案by BalusC
That's micro optimization and premature optimization, which are evil. Rather worry about readabililty and maintainability of the code in question. If there are more than two if/else
blocks glued together or its size is unpredictable, then you may highly consider a switch
statement.
那是微优化和过早优化,这是邪恶的。而是担心相关代码的可读性和可维护性。如果有两个以上的if/else
块粘在一起或者它的大小是不可预测的,那么你可能会高度考虑一个switch
陈述。
Alternatively, you can also grab Polymorphism. First create some interface:
或者,您也可以获取Polymorphism。首先创建一些接口:
public interface Action {
void execute(String input);
}
And get hold of all implementations in some Map
. You can do this either statically or dynamically:
并在某些Map
. 您可以静态或动态地执行此操作:
Map<String, Action> actions = new HashMap<String, Action>();
Finally replace the if/else
or switch
by something like this (leaving trivial checks like nullpointers aside):
最后将if/else
or替换为switch
这样的(将空指针之类的琐碎检查放在一边):
actions.get(name).execute(input);
It mightbe microslower than if/else
or switch
, but the code is at least far better maintainable.
它可能比if/else
或慢switch
,但代码至少可维护性好得多。
As you're talking about webapplications, you can make use of HttpServletRequest#getPathInfo()
as action key (eventually write some more code to split the last part of pathinfo away in a loop until an action is found). You can find here similar answers:
当您在谈论 Web 应用程序时,您可以使用HttpServletRequest#getPathInfo()
as 操作键(最终编写更多代码以将 pathinfo 的最后一部分拆分为循环,直到找到操作)。你可以在这里找到类似的答案:
If you're worrying about Java EE webapplication performance in general, then you may find this articleuseful as well. There are other areas which gives a much moreperformance gain than only (micro)optimizing the raw Java code.
如果您通常担心 Java EE Web 应用程序的性能,那么您可能会发现本文也很有用。与仅(微)优化原始 Java 代码相比,还有其他领域可以提供更多的性能提升。
回答by John Feminella
It's extremely unlikely that an if/else or a switch is going to be the source of your performance woes. If you're having performance problems, you should do a performance profiling analysis first to determine where the slow spots are. Premature optimization is the root of all evil!
if/else 或 switch 极不可能成为性能问题的根源。如果您遇到性能问题,您应该首先进行性能分析以确定慢点在哪里。过早优化是万恶之源!
Nevertheless, it's possible to talk about the relative performance of switch vs. if/else with the Java compiler optimizations. First note that in Java, switch statements operate on a very limited domain -- integers. In general, you can view a switch statement as follows:
尽管如此,还是可以通过 Java 编译器优化来讨论 switch 与 if/else 的相对性能。首先请注意,在 Java 中,switch 语句在一个非常有限的域上运行——整数。一般情况下,可以如下查看switch语句:
switch (<condition>) {
case c_0: ...
case c_1: ...
...
case c_n: ...
default: ...
}
where c_0
, c_1
, ..., and c_N
are integral numbers that are targets of the switch statement, and <condition>
must resolve to an integer expression.
where c_0
, c_1
, ..., andc_N
是作为 switch 语句目标<condition>
的整数,并且必须解析为整数表达式。
If this set is "dense" -- that is, (max(ci) + 1 - min(ci)) / n > α, where 0 < k < α < 1, where
k
is larger than some empirical value, a jump table can be generated, which is highly efficient.If this set is not very dense, but n >= β, a binary search tree can find the target in O(2 * log(n)) which is still efficient too.
如果这个集合是“密集的”——也就是说,(max( ci) + 1 - min( ci)) / n > α,其中 0 < k < α < 1,其中
k
大于某个经验值,a可以生成跳转表,效率很高。如果这个集合不是很密集,但是 n >= β,二叉搜索树可以在 O(2 * log(n)) 中找到目标,这仍然是有效的。
For all other cases, a switch statement is exactly as efficient as the equivalent series of if/else statements. The precise values of α and β depend on a number of factors and are determined by the compiler's code-optimization module.
对于所有其他情况,switch 语句与等效的一系列 if/else 语句完全一样有效。α 和 β 的精确值取决于许多因素,并由编译器的代码优化模块确定。
Finally, of course, if the domain of <condition>
is not the integers, a switch
statement is completely useless.
最后,当然,如果 的域<condition>
不是整数,那么 switch 语句是完全没用的。
回答by Waverick
I totally agree with the opinion that premature optimization is something to avoid.
我完全同意过早优化是应该避免的观点。
But it's true that the Java VM has special bytecodes which could be used for switch()'s.
但是 Java VM 确实具有可用于 switch() 的特殊字节码。
See WM Spec(lookupswitchand tableswitch)
请参阅WM 规范(lookupswitch和tableswitch)
So there could be some performance gains, if the code is part of the performance CPU graph.
因此,如果代码是性能 CPU 图表的一部分,则可能会有一些性能提升。
回答by Jim Ferrans
According to Cliff Click in his 2009 Java One talk A Crash Course in Modern Hardware:
根据 Cliff Click 在他 2009 年的 Java One 演讲中的现代硬件速成课程:
Today, performance is dominated by patterns of memory access. Cache misses dominate – memory is the new disk. [Slide 65]
今天,性能由内存访问模式决定。缓存未命中占主导地位 - 内存是新磁盘。[幻灯片 65]
You can get his full slides here.
您可以在此处获取他的完整幻灯片。
Cliff gives an example (finishing on Slide 30) showing that even with the CPU doing register-renaming, branch prediction, and speculative execution, it's only able to start 7 operations in 4 clock cycles before having to block due to two cache misses which take 300clock cycles to return.
Cliff 举了一个例子(在幻灯片 30 上完成)表明即使 CPU 进行寄存器重命名、分支预测和推测执行,它也只能在 4 个时钟周期内启动 7 个操作,然后由于两次缓存未命中而不得不阻塞300 个时钟周期返回。
So he says to speed up your program you shouldn't be looking at this sort of minor issue, but on larger ones such as whether you're making unnecessary data format conversions, such as converting "SOAP → XML → DOM → SQL → …" which "passes all the data through the cache".
所以他说为了加速你的程序,你不应该关注这类小问题,而应该关注更大的问题,比如你是否在进行不必要的数据格式转换,比如转换“SOAP → XML → DOM → SQL → ... “它“通过缓存传递所有数据”。
回答by malaverdiere
I remember reading that there are 2 kinds of Switch statements in Java bytecode. (I think it was in 'Java Performance Tuning' One is a very fast implementation which uses the switch statement's integer values to know the offset of the code to be executed. This would require all integers to be consecutive and in a well-defined range. I'm guessing that using all the values of an Enum would fall in that category too.
我记得读过 Java 字节码中有 2 种 Switch 语句。(我认为这是在“Java 性能调优”中的一个非常快速的实现,它使用 switch 语句的整数值来知道要执行的代码的偏移量。这将要求所有整数都是连续的并且在一个明确定义的范围内. 我猜使用 Enum 的所有值也属于该类别。
I agree with many other posters though... it may be premature to worry about this, unless this is very very hot code.
不过,我同意许多其他海报……现在担心这一点可能还为时过早,除非这是非常热门的代码。
回答by Bitterblue
Use switch!
使用开关!
I hate to maintain if-else-blocks! Have a test:
我讨厌维护 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));
}
}
回答by scottb
For most switch
and most if-then-else
blocks, I can't imagine that there are any appreciable or significant performance related concerns.
对于大多数switch
和大多数if-then-else
块,我无法想象有任何明显或重要的性能相关问题。
But here's the thing: if you're using a switch
block, its very use suggests that you're switching on a value taken from a set of constants known at compile time. In this case, you really shouldn't be using switch
statements at all if you can use an enum
with constant-specific methods.
但事情是这样的:如果您正在使用一个switch
块,它的使用表明您正在切换从编译时已知的一组常量中获取的值。在这种情况下,switch
如果您可以使用enum
特定于常量的方法,您真的不应该使用语句。
Compared to a switch
statement, an enum provides better type safety and code that is easier to maintain. Enums can be designed so that if a constant is added to the set of constants, your code won't compile without providing a constant-specific method for the new value. On the other hand, forgetting to add a new case
to a switch
block can sometimes only be caught at run time if you're lucky enough to have set your block up to throw an exception.
与switch
语句相比,枚举提供了更好的类型安全性和更易于维护的代码。枚举可以被设计成这样,如果一个常量被添加到常量集中,如果没有为新值提供特定于常量的方法,你的代码将无法编译。在另一方面,忘了一个新添加case
的switch
,有时只能在运行时被抓块,如果你足够幸运的都设置你的格挡了抛出异常。
Performance between switch
and an enum
constant-specific method should not be significantly different, but the latter is more readable, safer, and easier to maintain.
switch
和enum
特定于常量的方法之间的性能不应有显着差异,但后者更具可读性、更安全且更易于维护。
回答by Kanagavelu Sugumar
In my test the better performance is ENUM > MAP > SWITCH > IF/ELSE IFin Windows7.
在我的测试中,更好的性能是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
*/