Как строка в инструкции switch более эффективна, чем соответствующая инструкция if-else?

Документация по Java говорит

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

AFAIK даже String в коммутаторе использует .equals() внутренне чувствительным к регистру образом. Итак, какова эффективность в этом контексте. Быстрее компиляции? Меньше байткодов? более высокая производительность?

Ответ 1

Использование оператора switch быстрее, чем equals (но заметно только при наличии нескольких строк), потому что сначала использует hashCode строки, в которой switch для определения подмножества строк, которые могли бы возможно, совпадают. Если более чем одна строка в ярлыках case имеет один и тот же хэш-код, JVM будет выполнять последовательные вызовы equals, и даже если в метках case есть только одна строка, которая имеет hashCode, JVM необходимо вызвать equals для подтверждения что строка в метке case действительно равна той, что находится в выражении switch.

Производительность запуска коммутатора по объектам String сопоставима с поиском в HashMap.

Этот фрагмент кода:

public static void main(String[] args) {
    String s = "Bar";
    switch (s) {
    case "Foo":
        System.out.println("Foo match");
        break;
    case "Bar":
        System.out.println("Bar match");
        break;
    }
}

Внутренне скомпилирован и выполнен как этот кусок кода:

(не буквально, но если вы декомпилируете обе части кода, вы увидите, что происходит точная последовательность действий)

final static int FOO_HASHCODE = 70822; // "Foo".hashCode();
final static int BAR_HASHCODE = 66547; // "Bar".hashCode();

public static void main(String[] args) {
    String s = "Bar";
    switch (s.hashCode()) {
    case FOO_HASHCODE:
        if (s.equals("Foo"))
            System.out.println("Foo match");
        break;
    case BAR_HASHCODE:
        if (s.equals("Bar"))
            System.out.println("Bar match");
        break;
    }
}

Ответ 2

В общем операторы switch лучше, потому что они (свободно говоря) O(1), а цепочка if-else - O(n)

Наличие условий n может привести к сравнению с n сравнением с помощью цепочки if-else.

Оператор switch может "перейти" непосредственно к соответствующему условию (например, к карте) или к случаю по умолчанию, сделав его O(1).

Ответ 3

Это фрагмент байт-кода, сгенерированный из примера в документах:

 INVOKEVIRTUAL java/lang/String.hashCode ()I
    LOOKUPSWITCH
      -2049557543: L2
      -1984635600: L3
      -1807319568: L4

использование LOOKUPSWITCH имеет лучшую производительность по сравнению с логикой if-else