Java:If vs. Switch

我有一段代码与a)我用b)代替纯粹为了易读性…

一个)

if ( WORD[ INDEX ] == 'A' ) branch = BRANCH.A; /* B through to Y */ if ( WORD[ INDEX ] == 'Z' ) branch = BRANCH.Z; 

b)

 switch ( WORD[ INDEX ] ) { case 'A' : branch = BRANCH.A; break; /* B through to Y */ case 'Z' : branch = BRANCH.Z; break; } 

…开关版本是否会级联所有排列或跳转到案例?

编辑:

下面的一些答案涉及上述方法的替代方法。
我已经包含以下内容以提供其使用的上下文。

我问的问题,上面的问题,是因为增加词的经验改进的速度。

这不是生产代码,而是作为PoC快速入侵。

以下似乎是对思想实验失败的证实。
我可能需要比我目前使用的词语更大的词汇。
失败的原因是我没有考虑仍需要内存的空引用。 (doh!)

 public class Dictionary { private static Dictionary ROOT; private boolean terminus; private Dictionary A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z; private static Dictionary instantiate( final Dictionary DICTIONARY ) { return ( DICTIONARY == null ) ? new Dictionary() : DICTIONARY; } private Dictionary() { this.terminus = false; this.A = this.B = this.C = this.D = this.E = this.F = this.G = this.H = this.I = this.J = this.K = this.L = this.M = this.N = this.O = this.P = this.Q = this.R = this.S = this.T = this.U = this.V = this.W = this.X = this.Y = this.Z = null; } public static void add( final String...STRINGS ) { Dictionary.ROOT = Dictionary.instantiate( Dictionary.ROOT ); for ( final String STRING : STRINGS ) Dictionary.add( STRING.toUpperCase().toCharArray(), Dictionary.ROOT , 0, STRING.length() - 1 ); } private static void add( final char[] WORD, final Dictionary BRANCH, final int INDEX, final int INDEX_LIMIT ) { Dictionary branch = null; switch ( WORD[ INDEX ] ) { case 'A' : branch = BRANCH.A = Dictionary.instantiate( BRANCH.A ); break; case 'B' : branch = BRANCH.B = Dictionary.instantiate( BRANCH.B ); break; case 'C' : branch = BRANCH.C = Dictionary.instantiate( BRANCH.C ); break; case 'D' : branch = BRANCH.D = Dictionary.instantiate( BRANCH.D ); break; case 'E' : branch = BRANCH.E = Dictionary.instantiate( BRANCH.E ); break; case 'F' : branch = BRANCH.F = Dictionary.instantiate( BRANCH.F ); break; case 'G' : branch = BRANCH.G = Dictionary.instantiate( BRANCH.G ); break; case 'H' : branch = BRANCH.H = Dictionary.instantiate( BRANCH.H ); break; case 'I' : branch = BRANCH.I = Dictionary.instantiate( BRANCH.I ); break; case 'J' : branch = BRANCH.J = Dictionary.instantiate( BRANCH.J ); break; case 'K' : branch = BRANCH.K = Dictionary.instantiate( BRANCH.K ); break; case 'L' : branch = BRANCH.L = Dictionary.instantiate( BRANCH.L ); break; case 'M' : branch = BRANCH.M = Dictionary.instantiate( BRANCH.M ); break; case 'N' : branch = BRANCH.N = Dictionary.instantiate( BRANCH.N ); break; case 'O' : branch = BRANCH.O = Dictionary.instantiate( BRANCH.O ); break; case 'P' : branch = BRANCH.P = Dictionary.instantiate( BRANCH.P ); break; case 'Q' : branch = BRANCH.Q = Dictionary.instantiate( BRANCH.Q ); break; case 'R' : branch = BRANCH.R = Dictionary.instantiate( BRANCH.R ); break; case 'S' : branch = BRANCH.S = Dictionary.instantiate( BRANCH.S ); break; case 'T' : branch = BRANCH.T = Dictionary.instantiate( BRANCH.T ); break; case 'U' : branch = BRANCH.U = Dictionary.instantiate( BRANCH.U ); break; case 'V' : branch = BRANCH.V = Dictionary.instantiate( BRANCH.V ); break; case 'W' : branch = BRANCH.W = Dictionary.instantiate( BRANCH.W ); break; case 'X' : branch = BRANCH.X = Dictionary.instantiate( BRANCH.X ); break; case 'Y' : branch = BRANCH.Y = Dictionary.instantiate( BRANCH.Y ); break; case 'Z' : branch = BRANCH.Z = Dictionary.instantiate( BRANCH.Z ); break; } if ( INDEX == INDEX_LIMIT ) branch.terminus = true; else Dictionary.add( WORD, branch, INDEX + 1, INDEX_LIMIT ); } public static boolean is( final String STRING ) { Dictionary.ROOT = Dictionary.instantiate( Dictionary.ROOT ); return Dictionary.is( STRING.toUpperCase().toCharArray(), Dictionary.ROOT, 0, STRING.length() - 1 ); } private static boolean is( final char[] WORD, final Dictionary BRANCH, final int INDEX, final int INDEX_LIMIT ) { Dictionary branch = null; switch ( WORD[ INDEX ] ) { case 'A' : branch = BRANCH.A; break; case 'B' : branch = BRANCH.B; break; case 'C' : branch = BRANCH.C; break; case 'D' : branch = BRANCH.D; break; case 'E' : branch = BRANCH.E; break; case 'F' : branch = BRANCH.F; break; case 'G' : branch = BRANCH.G; break; case 'H' : branch = BRANCH.H; break; case 'I' : branch = BRANCH.I; break; case 'J' : branch = BRANCH.J; break; case 'K' : branch = BRANCH.K; break; case 'L' : branch = BRANCH.L; break; case 'M' : branch = BRANCH.M; break; case 'N' : branch = BRANCH.N; break; case 'O' : branch = BRANCH.O; break; case 'P' : branch = BRANCH.P; break; case 'Q' : branch = BRANCH.Q; break; case 'R' : branch = BRANCH.R; break; case 'S' : branch = BRANCH.S; break; case 'T' : branch = BRANCH.T; break; case 'U' : branch = BRANCH.U; break; case 'V' : branch = BRANCH.V; break; case 'W' : branch = BRANCH.W; break; case 'X' : branch = BRANCH.X; break; case 'Y' : branch = BRANCH.Y; break; case 'Z' : branch = BRANCH.Z; break; } if ( branch == null ) return false; if ( INDEX == INDEX_LIMIT ) return branch.terminus; else return Dictionary.is( WORD, branch, INDEX + 1, INDEX_LIMIT ); } } 

在字节码中有两种forms的开关: tableswitchlookupswitch 。 一个假设密集的密钥,另一个密集。 请参阅JVM规范中的编译开关说明 。 对于枚举,找到序号,然后代码继续作为int情况。 我不完全确定如何实现JDK7中建议的String小function的switch

但是,大量使用的代码通常在任何合理的JVM中编译。 优化者并非完全愚蠢。 不要担心,并按照通常的启发式方法进行优化。

不要担心性能; 使用最能表达您正在做的事情的语法。 只有在你(a)表现出性能不足之后; (b)将其本地化为相关例程,只有这样才能担心性能问题。 对于我的钱,案例语法在这里更合适。

看起来你已经枚举了这些值,所以也许枚举是有序的?

 enum BRANCH { A,B, ... Y,Z; } 

然后在你的代码中:

 BRANCH branch = BRANCH.valueOf( WORD[ INDEX ] ); 

此外,您的代码中可能存在错误,其中"A" == "A"可能是错误的,具体取决于"A" == "A"的对象标识。

对你的问题不是一个完全答案,但你的代码中确实存在一个错误,你应该在每个案例后rest一下:

 switch ( WORD[ INDEX ] ) { case 'A' : branch = BRANCH.A; break; /* B through to Y */ case 'Z' : branch = BRANCH.Z; break; } 

我不认为这里的性能差异太大,但如果你真的关心性能,并且如果这个代码经常执行,那么这是另一个选择:

 // Warning, untested code. BRANCH[] branchLookUp = {BRANCH.A, BRANCH.B, ..., BRANCH.Z}; branch = branchLookUp[WORD[INDEX] - 'A']; 

请务必将其封装并记录下来。

老实说,我不认为这种情况下的表现很重要。 这完全取决于编译器及其优化。

如果您有一个带有连续积分值的switch语句,具体取决于语言,它可以优化为分支表,这非常快。 反正它并不慢!

另外,对于诸如此类案例互相排斥的情况,使用if / else if是否会有所改进。 在匹配A之后再做25次检查没有意义。

但基本上,任何性能差异都可以忽略不计,你应该使用最正确的语法,在本例中是switch语句。 尽管如此,请确保将您的案件分开。

这是一种避免所有案例陈述的方法。

 import java.util.HashMap; public class Dictionary { private static Dictionary ROOT; private boolean terminus; private final HashMap dictionaries = new HashMap(); private void ensureBranch(char c) { if (getBranch(c) != null) return; dictionaries.put(c, new Dictionary()); } private Dictionary getBranch(char c) { return dictionaries.get(c); } public static boolean is(final String string) { ensureRoot(); return is(chars(string), ROOT, 0, string.length() - 1); } public static void add(final String... strings) { ensureRoot(); for (final String string : strings) add(chars(string), ROOT, 0, string.length() - 1); } private static void ensureRoot() { if (ROOT == null) ROOT = new Dictionary(); } private static char[] chars(final String string) { return string.toUpperCase().toCharArray(); } private Dictionary() { this.terminus = false; } private static void add(final char[] word, final Dictionary dictionary, final int index, final int limit) { Dictionary branch = getBranch(word, dictionary, index); if (index == limit) branch.terminus = true; else add(word, branch, index + 1, limit); } private static Dictionary getBranch(final char[] word, final Dictionary dictionary, final int index) { final char c = word[index]; dictionary.ensureBranch(c); return dictionary.getBranch(c); } private static boolean is(final char[] word, final Dictionary dictionary, final int index, final int limit) { Dictionary branch = dictionary.getBranch(word[index]); if (branch == null) return false; if (index == limit) return branch.terminus; return is(word, branch, index + 1, limit); } } 

我知道这不是你要求的,但你不是这样做的吗?

 public class Dictionary { private static final Set WORDS = new HashSet(); public static void add(final String... STRINGS) { for (String str : STRINGS) { WORDS.add(str.toUpperCase()); } } public static boolean is(final String STRING) { return WORDS.contains(STRING.toUpperCase()); } } 

您只是在寻找更高效的内存吗?

switch语句应使用散列来选择要转到的大小写。 从那里开始,如果没有break语句,也会运行每个后续案例。 例如,使用您的代码,如果您打开X,它将立即转到X,然后是Y,然后转到Z.请参阅Java教程 。

switch应该是对数的, if线性的,假设编译器找不到任何聪明的东西。 但是长时间的switch很难阅读并且容易出错 – 如上所述,你上面的开关没有任何中断,并且它会在所有情况下失败。

为什么不预先填充Map ,只使用Map.get()

 private static final Map BRANCHES = Collections.unmodifiableMap(new HashMap() {{ put('A', BRANCH.A); ... put('Z', BRANCH.Z); }} public void getBranch(char[] WORD, int INDEX) { return BRANCHES.get(WORD[INDEX]); } 

如上所述,如果BRANCHEnum ,则此行为应恰好位于Enum

(无论如何,这里的WORDINDEXBRANCH是什么?从名称来看,它们应该是常量,但是你不能真正拥有常量数组 – 内容是可修改的alwyas;创建常量不会有太多意义“ struct“;并且在基于常量的iffing或切换中肯定没有多大意义….)

我认为这更多的是关于风格而不是表现的问题。 我认为在这种情况下,switch语句比if更合适。 我不确定性能有多大差异。