17

我有一段带有 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 被迅速破解。

以下似乎是对思想实验失败的确认。
不过,我可能需要比我目前使用的更大的词库。
失败的原因是我没有考虑仍然需要内存的空引用。 (呸!)

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

11 回答 11

24

不用担心性能;使用最能表达您正在做什么的语法。只有在您 (a) 表现出表现缺陷之后;(b) 将其本地化到有问题的例程中,然后您才应该担心性能。为了我的钱,这里的 case 语法更合适。

于 2009-06-29T23:46:13.017 回答
23

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

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

于 2009-06-29T23:49:21.810 回答
7

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

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

然后在您的代码中:

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

此外,您的代码中"A" == "A"可能存在错误,取决于“A”的对象身份,该错误可能是错误的。

于 2009-06-30T03:29:52.380 回答
4

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

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'];

请务必将其封装并妥善记录。

于 2009-06-29T23:57:45.367 回答
3

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

于 2009-06-29T23:47:16.510 回答
3

如果你有一个带有连续整数值的 switch 语句,根据语言的不同,它可能会优化为一个分支表,这非常快。反正也不慢!

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

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

于 2009-06-29T23:48:07.990 回答
3

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

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);
    }
}
于 2009-06-30T17:31:44.383 回答
2

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

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

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

于 2009-07-02T15:55:23.763 回答
1

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

于 2009-06-29T23:46:33.920 回答
1

switch应该是对数和线性的,if假设编译器找不到任何聪明的东西。但是 long switches 难以阅读并且容易出错 - 如前所述,您上面的开关没有任何中断,并且它将在所有情况下都失败。

为什么不预先填充 a Map,而只使用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]);
}

如上所述,如果BRANCHEnum,则此行为应正确位于Enum.

(无论如何WORD,这里是什么?从名称来看INDEXBRANCH它们应该是常量,但你不能真正拥有常量数组——内容总是可以修改的;创建一个常量“结构”没有多大意义;并且基于常数的iffing或切换肯定没有多大意义....)

于 2009-06-30T08:52:26.930 回答
0

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

于 2009-06-30T02:09:40.387 回答