Java:If vs. Switch

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1061101/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 14:59:27  来源:igfitidea点击:

Java: If vs. Switch

javacomparisonswitch-statementif-statement

提问by Ande Turner

I have a piece of code with a) which I replaced with b) purely for legibility ...

我有一段带有 a) 的代码,我用 b) 替换了它,纯粹是为了可读性......

a)

一个)

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

b)

b)

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



... will the switch version cascade through all the permutations or jump to a case ?

... switch 版本会通过所有排列级联还是跳转到一个案例?



EDIT:



编辑:

Some of the answers below regard alternative approaches to the approach above.
I have included the following to provide context for its use.

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

The reason I asked, the Question above, was because the speed of adding words empirically improved.

我问的原因,上面的问题,是因为经验上提高了添加单词的速度。

This isn't production code by any means, and was hacked together quickly as a PoC.

这无论如何都不是生产代码,而是作为 PoC 迅速被黑客攻击的。

The following seems to be a confirmation of failure for a thought experiment.
I may need a much bigger corpus of words than the one I am currently using though.
The failure arises from the fact I did not account for the null references still requiring memory.( 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 );
    }
}

回答by Carl Manaster

Don't worry about performance; use the syntax that best expresses what you're doing. Only after you have (a) demonstrated a performance deficiency; and (b) localized it to the routine in question, only then should you worry about performance. For my money, the case syntax is more appropriate here.

不用担心性能;使用最能表达您正在做的事情的语法。只有在您 (a) 表现出表现缺陷之后;(b) 将其本地化到有问题的例程,只有这样你才应该担心性能。对我来说,case 语法在这里更合适。

回答by Tom Hawtin - tackline

In bytecode there are two forms of switch: tableswitchand lookupswitch. One assumes a dense set of keys, the other sparse. See the description of compiling switch in the JVM spec. For enums, the ordinal is found and then the code continues as the intcase. I am not entirely sure how the proposed switchon Stringlittle feature in JDK7 will be implemented.

在字节码中有两种形式的开关:tableswitchlookupswitch。一个假设一组密集的密钥,另一个稀疏。请参阅JVM 规范中编译开关的描述。对于枚举,找到序数,然后代码按int情况继续。我不完全确定JDK7 中提议switchString小功能将如何实现。

However, heavily used code is typically compiled in any sensible JVM. The optimiser is not entirely stupid. Don't worry about it, and follow the usual heuristics for optimisation.

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

回答by Clint

It looks like you have enumerated the values so perhaps an enumeration is in order?

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

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

Then in your code :

然后在你的代码中:

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

Also, there is a possible bug in your code where "A" == "A"may be false depending on the object identity of the "A"'s.

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

回答by Hyman Leow

Not quite an answer to your question, but there's actually a bug in your code, you should have a break after each case:

不是你问题的答案,但实际上你的代码中有一个错误,你应该在每个案例之后休息一下:

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

I don't think performance differences are going to be too significant here, but if you really care about performance, and if this code is executed very frequently, here's another option:

我认为这里的性能差异不会太大,但是如果您真的关心性能,并且如果此代码执行非常频繁,那么这里有另一种选择:

// Warning, untested code.
BRANCH[] branchLookUp = {BRANCH.A, BRANCH.B, ..., BRANCH.Z};

branch = branchLookUp[WORD[INDEX] - 'A'];

Be sure to encapsulate this and document it well though.

一定要封装它并记录它。

回答by Cambium

Honestly, I don't think the performance matters in this case. It's really up to the compiler and its optimization.

老实说,我认为在这种情况下性能并不重要。这真的取决于编译器及其优化。

回答by Nick Lewis

If you have a switch statement with consecutive, integral values, depending on the language, it may be optimized to a branch table, which is very quick. It's not slower, anyway!

如果你有一个带有连续整数值的 switch 语句,取决于语言,它可能被优化为一个分支表,这非常快。无论如何,它并不慢!

Additionally, using if/else if would be an improvement over if, for cases such as this one in which your cases are mutually exclusive. No sense making 25 more checks after matching A.

此外,使用 if/else if 将是对 if 的改进,对于这种情况,例如您的情况是相互排斥的。在匹配 A 之后再做 25 次检查是没有意义的。

But basically, any performance difference is negligible, and you ought to use the most correct syntax, which in this case is the switch statement. Make sure to separate your cases with breaks though.

但基本上,任何性能差异都可以忽略不计,您应该使用最正确的语法,在这种情况下是 switch 语句。不过,请确保将您的案例与休息区分开。

回答by Carl Manaster

Here's a way to avoid all of the case statements.

这是一种避免所有 case 语句的方法。

import java.util.HashMap;

public class Dictionary {
    private static Dictionary                       ROOT;
    private boolean                                 terminus;
    private final HashMap<Character, Dictionary>    dictionaries    = new HashMap<Character, Dictionary>();

    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);
    }
}

回答by Hyman Leow

I know this is not what you are asking at all, but aren't you just doing this?

我知道这根本不是你要问的,但你不就是这样做吗?

public class Dictionary {
    private static final Set<String> WORDS = new HashSet<String>();

    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());
    }
}

Are you simply looking for something a little more memory efficient?

您是否只是在寻找内存效率更高的东西?

回答by David Johnstone

The switchstatement should use a hash to select which case to go to. From there, every subsequent case will also be run if there are no breakstatements. For example, with your code, if you switch on X, it will immediately go to X, then Y, then Z. See the Java Tutorial.

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

回答by David Moles

The switchshould be logarithmic and the iflinear, assuming the compiler can't find anything clever. But long switches are tricky to read and also bug-prone -- as noted, the switch you've got above doesn't have any breaks, and it's going to fall through all the cases.

switch应是对数和if线性,假设编译器无法找到任何聪明。但是 long switches 阅读起来很棘手,而且容易出错——如上所述,你上面的开关没有任何中断,而且它会在所有情况下失败。

Why not prepopulate a Mapinstead, and just use Map.get()?

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

private static final Map<Char, Whatever> BRANCHES = Collections.unmodifiableMap(new HashMap<Char, Whatever>() {{
    put('A', BRANCH.A);
    ...
    put('Z', BRANCH.Z);
}}

public void getBranch(char[] WORD, int INDEX) {
    return BRANCHES.get(WORD[INDEX]);
}

As noted above, if BRANCHis an Enum, this behavior should properly be in the Enum.

如上所述,如果BRANCHEnum,则此行为应该正确地在Enum.

(What are WORD, INDEXand BRANCHhere, anyway? From the names, they should be constants, but you can't really have constant arrays -- the contents are alwyas modifiable; there wouldn't be much point in creating a constant "struct"; and there certainly wouldn't be much point in iffing or switching based on constants....)

(什么是WORDINDEXBRANCH在这里,反正从名字,他们应该是常数,但你不能真的有恒定的阵列-内容是alwyas修改;就不会有创造一个恒定的“结构”多点;并且基于常量进行 iffing 或切换当然没有什么意义....)