Оптимизация if-else / switch-case со строковыми параметрами - PullRequest
4 голосов
/ 08 октября 2009

Какая модификация принесет этот кусок кода? В последних строках я должен использовать больше if-else структур вместо " if-if-if"

if (action.equals("opt1"))
{
    //something
}
else
{
    if (action.equals("opt2"))
    {
        //something
    }
    else
    {
        if ((action.equals("opt3")) || (action.equals("opt4")))
        {
            //something
        }
        if (action.equals("opt5"))
        {
            //something
        }
        if (action.equals("opt6"))
        {
            //something
        }
    }
}

Позднее редактирование: это Java. Я не думаю, что структура переключателя будет работать со строками.

Позже Редактировать 2:

Переключатель работает с байтом, коротким, char и int примитивные типы данных. Это также работает с перечисляемыми типами (обсуждается в классах и наследовании) и несколько специальных классов, которые "обертывают" определенные примитивные типы: характер, Byte, Short и Integer (обсуждается в простых объектах данных).

Ответы [ 13 ]

0 голосов
/ 08 октября 2009

Такое процедурное переключение очень часто лучше обрабатывается полиморфизмом - вместо того, чтобы иметь действие, представленное строкой, представлять действие объекта, у которого есть метод «что-то», который вы можете специализировать. Если вы обнаружите, что вам нужно сопоставить строку с параметром, используйте Map<String,Option>.

Если вы хотите придерживаться процедурного кода, и параметры в вашем реальном коде на самом деле все "optX":

if ( action.startsWith("opt") && action.length() == 4 ) {
    switch ( action.charAt(3) ) {
        case '1':  something; break;
        case '2':  something; break;
        case '3':  something; break;
        ...
    }
}

, который был бы приемлем в парсере (где разбивка строк является частью проблемной области), и должен быть быстрым, но не связным (связь между объектом action и поведением основана на части его представления, а не что-либо присущее объекту).

0 голосов
/ 08 октября 2009

На самом деле это зависит от отраслевого анализа. Если 99% ваших решений "opt1", этот код уже довольно хорош. Если 99% ваших решений "opt6", этот код ужасно плох.

Если вы часто получаете «opt6» и редко «opt1», поместите «opt6» в первое сравнение и упорядочите следующие сравнения в соответствии с частотой строк в вашем потоке данных выполнения.

Если у вас много параметров и все они имеют одинаковую частоту, вы можете отсортировать параметры и разбить их на двоичное дерево, например:

if (action < "opt20")
{
   if( action < "opt10" )
   {
     if( action  ==  "opt4" ) {...}
     else if( action  ==  "opt2" ) {...}
     else if( action  ==  "opt1" ) {...}
     else if( action  ==  "opt8" ) {...}
   }
}
else
{
    if( action < "opt30 )
    {

    }
    else
    {
      if( action  ==  "opt38" ) {...}
      else if( action  ==  "opt32" ) {...}
    }
}

В этом примере деление диапазона уменьшает количество необходимых сравнений для «opt38» и «opt4» до 3. Таким образом, вы получаете log2 (n) +1 сравнений в каждой ветви. это лучше для равных частот вариантов.

Не делайте бинарную слюну до конца, в конце используйте 4-10 «нормальных» значений, если конструкции упорядочены по частоте опций. Последние два или три уровня в двоичном дереве не требуют значительных успехов.

Резюме

По крайней мере, есть две оптимизации для такого рода сравнений.

  1. Двоичные деревья решений
  2. Заказ из-за частоты опций

Двоичное дерево решений используется компилятором для больших конструкций switch-case. Но компилятор ничего не знает о частотах опции. Таким образом, упорядочение по частотам может быть преимуществом производительности при использовании распределительного шкафа, если один или два варианта встречаются гораздо чаще, чем другие. В данном случае это обходной путь:

if (action == "opt5") // Processing a frequent (99%) option first
{
}
else // Processing less frequent options (>1%) second
{
    switch( action )
    {
    case "opt1": ...
    case "opt2": ...
    }
}

Внимание

Не оптимизируйте свой код, пока не выполните профилирование, и это действительно необходимо. Лучше всего использовать switch-case или else-если прямо, и ваш код остается чистым и читаемым. Если вы оптимизировали свой код, поместите несколько хороших комментариев в код, чтобы каждый мог понять этот безобразный мир кода. Через год вы не будете знать данные профилирования, и некоторые комментарии будут действительно полезны.

0 голосов
/ 08 октября 2009

Ответы, рекомендующие использование оператора switch, - это путь. Оператор switch гораздо легче читать, чем беспорядок в операторах if и if...else, которые есть у вас сейчас.

Простые сравнения быстрые, и код //something не будет выполняться для всех случаев, кроме одного, поэтому вы можете пропустить «оптимизацию» и перейти к «ремонтопригодности».

Конечно, это предполагает, что метод action.equals() делает что-то тривиальное и недорогое, как ==. Если action.equals() стоит дорого, у вас есть другие проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...