Java: производительность Enums по сравнению с if-then-else

Мне не повезло получить краткий ответ для этого сравнения с помощью Google, и вместо того, чтобы делать собственные трудоемкие оценки, я решил сначала спросить.

Я почти уверен, что оператор switch с использованием Enums будет работать быстрее, чем оператор if-then-else, хотя другой вопрос, является ли это заметной разницей.

Может ли кто-нибудь пролить свет на это для меня?


Спасибо за быстрые ответы, ребята, я буду иметь это в виду для будущих проектов.


person FizzBuzz    schedule 07.02.2011    source источник
comment
Я очень сомневаюсь, что разница в производительности будет чем-то большим, чем микрооптимизация. Я бы выбрал переключатель stmt, если в вашем случае он обеспечивает более читаемый и удобный код.   -  person CoolBeans    schedule 07.02.2011
comment
Мне любопытно, почему вы говорите, что я совершенно уверен, что оператор switch с использованием Enums будет работать быстрее, чем оператор if-then-else?   -  person Pops    schedule 07.02.2011
comment
@Lord Torgamus - в операторе switch каждый случай является либо истинным, либо ложным, в операторе if-then-else каждый if может иметь несколько логических значений, то есть это && это или это || это и т. д. И я просто полагаю, что оценка более чем одного займет немного больше времени.   -  person FizzBuzz    schedule 07.02.2011
comment
Мне любопытно, можете ли вы получить какую-либо измеримую разницу. Для такого микробенчмаркинга я бы рекомендовал code.google.com/p/caliper, так как это очень прост в использовании и разработан ребятами из Guava. В случае, если вы измерите его, дайте знать результаты.   -  person maaartinus    schedule 07.02.2011
comment
На самом деле дело не в производительности, имхо - какой из двух легче читается, проще в обслуживании, менее подвержен ошибкам? Это должно быть более важной мотивацией для выбора одного из них, чем производительность. Я почти уверен, что вы уже догадались, что различия в производительности будут в лучшем случае минимальными.   -  person cthulhu    schedule 07.02.2011
comment
Дополнительное примечание: рассмотрите возможность замены условных операторов полиморфизмом (перечисления Java поддерживают даже абстрактные методы): refactoring.com/catalog /replaceConditionalWithPolymorphism.html   -  person Puce    schedule 07.02.2011


Ответы (6)


Да, это так, потому что в общем случае оператор switch работает быстрее, чем цепочка if/else.

Хотя сгенерированный байт-код не всегда является окончательным источником для сравнения производительности, вы можете изучить его, чтобы получить лучшее представление.

Например, этот код:

class A { 
    enum N { ONE, TWO, THREE }
    void testSwitch( N e ) { 
        switch( e ) { 
            case ONE : x(); break;
            case TWO : x(); break;
            case THREE : x(); break;
        }
    }
    void testIf( Enum e ) { 
        if( e == N.ONE ) { x(); }
        else if( e == N.TWO ) { x(); }
        else if( e == N.THREE ) { x(); }
    }
    void x(){}
}

Генерирует следующее:

Compiled from "A.java"
class A extends java.lang.Object{
A();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

void testSwitch(A$N);
  Code:
   0:   getstatic   #2; //Field A$1.$SwitchMap$A$N:[I
   3:   aload_1
   4:   invokevirtual   #3; //Method A$N.ordinal:()I
   7:   iaload
   8:   tableswitch{ //1 to 3
        1: 36;
        2: 43;
        3: 50;
        default: 54 }
   36:  aload_0
   37:  invokevirtual   #4; //Method x:()V
   40:  goto    54
   43:  aload_0
   44:  invokevirtual   #4; //Method x:()V
   47:  goto    54
   50:  aload_0
   51:  invokevirtual   #4; //Method x:()V
   54:  return

void testIf(java.lang.Enum);
  Code:
   0:   aload_1
   1:   getstatic   #5; //Field A$N.ONE:LA$N;
   4:   if_acmpne   14
   7:   aload_0
   8:   invokevirtual   #4; //Method x:()V
   11:  goto    39
   14:  aload_1
   15:  getstatic   #6; //Field A$N.TWO:LA$N;
   18:  if_acmpne   28
   21:  aload_0
   22:  invokevirtual   #4; //Method x:()V
   25:  goto    39
   28:  aload_1
   29:  getstatic   #7; //Field A$N.THREE:LA$N;
   32:  if_acmpne   39
   35:  aload_0
   36:  invokevirtual   #4; //Method x:()V
   39:  return

void x();
  Code:
   0:   return

}

Что кажется довольно быстрым в обоих случаях.

Так что выбирайте тот, за которым легче ухаживать.

person OscarRyz    schedule 07.02.2011
comment
хотя переключатели, как правило, быстрее, пример того, когда переключатель не так хорош, - это когда операторы case разрежены и / или не по порядку, тогда он может потерять свои преимущества... но, как всегда с любой микрооптимизацией - тесты времени единственный верный ответ :-) - person SteelBytes; 07.02.2011
comment
Три значения перечисления не будут иметь значения. Попробуйте со 100, и вы увидите один поиск по таблице против 100 сравнений. - person OrangeDog; 07.02.2011
comment
@Orange И, конечно же, вызывать разные методы в каждом из них. - person OscarRyz; 07.02.2011

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

person Mikko Wilkman    schedule 07.02.2011

Я не знаю о более быстром, я предполагаю, что они оба невероятно быстры.

Я считаю, что переключатель с перечислениями намного читабельнее, чем блок multi-if/else.

Но остерегайтесь пропущенных операторов break!!

person Sean Patrick Floyd    schedule 07.02.2011

Да, оператор switch почти всегда будет выполняться быстрее, чем эквивалентный блок операторов if/else, потому что компилятор может выполнять больше оптимизаций (обычно блок switch компилируется в таблицу ветвлений, что практически невозможно сделать с блоком условности.)

Я бы сказал, что они также более удобочитаемы и удобны в сопровождении (за исключением случаев с провалами, против которых я бы советовал!)

Что касается того, заметно ли это быстрее, это зависит от того, что вы определяете как заметное. Скорее всего, если вы не ищете что-то действительно конкретное, вы вообще этого не заметите, но я бы все равно поступал так из-за преимущества читабельности больше, чем чего-либо еще (рассматривайте преимущество в скорости как бонус!)

person Michael Berry    schedule 07.02.2011
comment
(обычно блок переключателей компилируется в таблицу ветвлений, что практически невозможно сделать с блоком условных выражений.) - но если компилятор достаточно умен, чтобы увидеть, что это просто список переменных по сравнению с (числовыми) константами ( что такое перечисления глубоко внутри), не сможет ли он оптимизировать его так же, как если бы это был переключатель/кейс? - person cthulhu; 07.02.2011
comment
@Cthulhu - я должен был сказать, что это невозможно сделать с общим случаем блока условных выражений. Да, в случае, который вы описываете, теоретически это было бы так, хотя я не уверен, что компилятор действительно выполняет этот анализ (ИМО, в любом случае, стоит сосредоточиться на оптимизации получше, чем на этой). - person Michael Berry; 07.02.2011

Мой ответ на это такой же, как и всегда на вопрос, является ли языковая конструкция X обычно быстрее, чем языковая конструкция Y: общего ответа нет!

Может быть только конкретный ответ для определенных реализаций языка, например. JVM на основе Hotspot-компилятора Oralce (формально Sun) или для IBM JDK на платформе Z, или для OpenJDK в Linux, или...

Таким образом, единственный способ дать осмысленный ответ на ваш вопрос — провести правильный тест. Остерегайтесь микротестов, они чаще ошибочны, чем верны, см., например, Как не стоит писать микротест. Если вы все еще хотите узнать об использовании этого вопроса, используйте структуру, описанную здесь.

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

person jmg    schedule 07.02.2011

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

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

person OrangeDog    schedule 07.02.2011
comment
Из памяти он выполняет этот расчетный переход (он может легко сделать это, потому что он ограничивает типы, которые можно использовать при переключении, примитивами, перечислениями и (в Java 7 и выше) строками. - person Michael Berry; 07.02.2011
comment
@ berry120 - Я думал об оптимальной оптимизации (дополнить случаи пустыми словами, а затем перейти вперед enum.ordinal() * sizeOfCase). - person OrangeDog; 07.02.2011