web-dev-qa-db-ja.com

switchステートメントのStringは、対応するif-elseステートメントよりも効率的ですか?

Javaドキュメンテーション は言う

Javaコンパイラーは、一般にチェーン化されたif-then-elseステートメントよりもStringオブジェクトを使用するswitchステートメントからより効率的なバイトコードを生成します。

知る限り、スイッチ内の文字列は、大文字と小文字を区別する方法で内部的に.equals()を使用します。そのため、このコンテキストでは効率とはどういう意味ですか。より高速なコンパイル?バイトコードが少ない?よりよい性能?

59
Aniket Thakur

Switchステートメントの使用は、同等よりも高速です(ただし、文字列が数個以上ある場合にのみ顕著です)。最初にhashCodeがオンになっている文字列のswitchを使用して、一致する可能性のある文字列のサブセットを決定します。ケースラベルの複数の文字列に同じhashCodeがある場合、JVMはequalsの順次呼び出しを実行し、hashCodeであるケースラベルに1つの文字列しかない場合でも、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;
    }
}
72
Erwin Bolwidt

generalでは、switch文は(おおざっぱに言えば)O(1)であるため優れていますが、_if-else_のチェーンはO(n)です

n条件があると、チェーンの_if-else_ステートメントを使用して、最大nの比較が発生する可能性があります。

Switchステートメントは、適切な条件(マップなど)またはデフォルトのケースに直接「ジャンプ」して、O(1)にすることができます。

19
Tyler

これは、ドキュメントの例から生成されたバイトコードフラグメントです。

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

lOOKUPSWITCHを使用すると、if-elseロジックに比べてパフォーマンスが向上します。

7