Вопрос по java, if-statement – Java: если против Switch

17

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

a)

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


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



РЕДАКТИРОВАТЬ:

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.

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.  (дох!)

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 );
    }
}
Я знаю, что вы не делаете этого в своем коде, но это может легко произойти в других случаях: если у вашего кода есть побочные эффекты (такие как изменение INDEX), то использование подхода If может запустить несколько ветвей, тогда как Switch не будет работать. , Я думаю, что Switch лучше передает цель и ее обязательно следует использовать (ну, это или словарный подход, предложенный людьми). Erich Mirabal
@MikeDaniels: исправлены пропущенные разрывы, было опущение транскрипции. Ande TURNER
Вам нужно сделать там несколько перерывов. Mike Daniels
@erickson: ура ... заставил меня добавить комментарий к сообщению о включении строкиblog.bpsite.net/item/71/Switch%20on%20String%20in%20Java.html Ande TURNER
Мой ответ на связанный вопрос может быть полезным:stackoverflow.com/questions/338206/… erickson

Ваш Ответ

11   ответов
1

switch должен быть логарифмическим иif линейный, предполагая, что компилятор не может найти ничего умного. Но долгоswitchИх сложно читать, а также они подвержены ошибкам - как уже отмечалось, у переключателя, который вы получили выше, нет никаких разрывов, и он будет падать во всех случаях.

Почему бы не заселить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]);
}

Как отмечено выше, еслиBRANCH являетсяEnumэто поведение должно быть правильно вEnum.

(ЧтоWORD, INDEX а такжеBRANCH здесь, в любом случае? Исходя из названий, они должны быть константами, но вы не можете иметь постоянные массивы - содержимое всегда можно изменить; не было бы особого смысла в создании константы "struct"; и, конечно, не будет особого смысла в переключении или переключении на основе констант ....)

24

используйте синтаксис, который лучше всего отражает то, что вы делаете. Только после того, как вы (а) продемонстрировали недостаток производительности; и (б) локализовать его для рассматриваемой процедуры, только тогда вы должны беспокоиться о производительности. За мои деньги синтаксис регистра здесь более уместен.

1

switch оператор должен использовать хеш, чтобы выбрать, в какой случай перейти. Оттуда, каждый последующий случай также будет запущен, если нетbreak заявления. Например, с вашим кодом, если вы включите X, он сразу перейдет к X, затем Y, затем Z.Учебник по Java.

3

я не думаю, что производительность имеет значение в этом случае. Это действительно зависит от компилятора и его оптимизации.

0

что это больше вопрос стиля, а не производительности. , что в этом случае оператор switch более уместен, чем если бы. Я не уверен, что есть большая разница в производительности.

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

Вы просто ищете что-то более эффективное использование памяти?

@JackLeow: было скучно ... А что касается экономии памяти, эта версия ваша выигрывает. Возможно, это должно стать вопросом игры в гольф. Ande TURNER
3

чениями, в зависимости от языка, он может быть оптимизирован для таблицы ветвлений, что очень быстро. Во всяком случае, это не медленнее!

Кроме того, использование if / else if будет улучшением по сравнению с if для таких случаев, как этот, в которых ваши случаи являются взаимоисключающими. Нет смысла делать еще 25 проверок после сопоставления А.

Но в принципе любая разница в производительности незначительна, и вы должны использовать наиболее правильный синтаксис, который в данном случае является оператором switch. Не забудьте разделить ваши дела с перерывами, хотя.

7

вы перечислили значения, так что, возможно, перечисление в порядке?

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

Тогда в вашем коде:

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

Кроме того, в вашем коде может быть ошибка"A" == "A" может быть ложным в зависимости от идентичности объекта "A".

21

tableswitch а такжеlookupswitch, Один предполагает плотный набор ключей, другой редкий. Смотрите описаниепереключатель компиляции в спецификации JVM, Для перечислений найден порядковый номер, а затем код продолжается какint дело. Я не совсем уверен, как предложенныйswitch наString небольшая функция в JDK7 будет реализована.

Однако интенсивно используемый код обычно компилируется в любой разумной JVM. Оптимизатор не совсем тупой. Не беспокойтесь об этом и следуйте обычной эвристике для оптимизации.

@ TomHawtin: Ура, Том. Ваши комментарии, как всегда, стоят на весу в соли. Я искал какую-то причину скачка скорости при выборе времени моих методов. Re: переключение на строкуblog.bpsite.net/item/71/Switch%20on%20String%20in%20Java.html Я обнаружил, что это полезно для чтения, однажды я написал фрагмент кода, который метакодировал версию enum из строковой версии, написанной Java-идиоматическим способом. Может быть, так оно и будет реализовано? Ande TURNER
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);
    }
}
@CarlManaster:: D Ande TURNER
@CarlManaster: я провел некоторые эксперименты. Это добавляет увеличение использования памяти примерно на 30%, немного большее время добавления новых слов и немного более быструю проверку записей. Ande TURNER
@CarlManaster: он составил 18410184 байт, использованных в оригинале, 25296416 байт, использованных с помощью Hashmap. Я использовал & quot; dictionary.txt & quot; доступно от Sun в их уроках, которые были 622783 байта на диске. Ande TURNER
@Ande: Спасибо, что сообщили мне (и всем нам) знать; Я рад, что вы используете эмпирический подход, и не удивлен, что вы обнаружили небольшие различия в скорости. Я нахожу увеличенное использование памяти немного удивительным; возможно, вы могли бы найти подходящую начальную емкость для хэш-карты, чтобы немного ее снизить.
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'];

Не забудьте инкапсулировать это и документировать это хорошо, хотя.

@JackLeow: исправлены пропущенные разрывы, было упущение транскрипции. Ande TURNER
+1 за предложение использовать массив. Вы должны рассмотреть вопрос о сохранении ФИЛИАЛОВ в массиве вместо 26 статических полей.

Похожие вопросы