1036

この機能は、後のJavaバージョンに組み込まれる予定ですか?

switchJavaのステートメントが機能する技術的な方法のように、なぜ私がこれを実行できないのか誰かが説明できますか?

4

14 に答える 14

1020

case を使用した switch ステートメントは、最初に要求されてから少なくとも 16 年後にJava SE 7Stringに実装されました。遅延の明確な理由は提供されませんでしたが、パフォーマンスに関係していた可能性があります。

JDK 7 での実装

javac この機能は、「脱糖」プロセスで実装されました。String宣言で定数を使用するクリーンで高レベルの構文caseは、コンパイル時にパターンに従ってより複雑なコードに展開されます。結果のコードは、常に存在する JVM 命令を使用します。

ケース付きのはswitchStringコンパイル中に 2 つのスイッチに変換されます。1 つ目は、各文字列を一意の整数 (元のスイッチでの位置) にマップします。これは、最初にラベルのハッシュ コードをオンにすることによって行われます。対応するケースは、if文字列の等価性をテストするステートメントです。ハッシュに衝突がある場合、テストはカスケードif-else-ifです。2 番目のスイッチは、元のソース コードを反映していますが、ケース ラベルを対応する位置に置き換えています。この 2 段階のプロセスにより、元のスイッチのフロー制御を簡単に維持できます。

JVM のスイッチ

の技術的な詳細については、switch ステートメントswitchのコンパイルについて説明されている JVM 仕様を参照してください。簡単に言えば、ケースで使用される定数のスパース性に応じて、スイッチに使用できる 2 つの異なる JVM 命令があります。どちらも、効率的に実行するために、ケースごとに整数定数を使用することに依存しています。

定数が密集している場合、それらは (最小値を減算した後) 命令ポインターのテーブル (命令) へのインデックスとして使用されますtableswitch

定数がスパースの場合、正しいケース (命令) のバイナリ検索が実行されlookupswitchます。

オブジェクトの de-sugarswitchではString、両方の命令が使用される可能性があります。は、ハッシュ コードの最初のスイッチで、ケースの元の位置を見つけるのlookupswitchに適しています。結果の序数は、 a に自然に適合しtableswitchます。

両方の命令で、各ケースに割り当てられた整数定数をコンパイル時にソートする必要があります。実行時には、一般にのO(1)パフォーマンスがのパフォーマンスよりも優れているように見えますが、空間と時間のトレードオフを正当化するのに十分なほどテーブルが密集しているかどうかを判断するには、何らかの分析が必要です。Bill Venners は、これをより詳細にカバーする素晴らしい記事を書き、他の Java フロー制御命令の内部を見ていきます。tableswitchO(log(n))lookupswitch

JDK7より前

JDK 7 より前では、 -ベースのスイッチにenum近似する可能性がありました。Stringこれは、すべての型でコンパイラによって生成された静的メソッドを使用します。valueOfenum例えば:

Pill p = Pill.valueOf(str);
switch(p) {
  case RED:  pop();  break;
  case BLUE: push(); break;
}
于 2008-12-03T18:30:45.677 に答える
129

コード内に文字列をオンにできる場所がある場合は、文字列をリファクタリングして、オンにできる可能な値の列挙にする方がよい場合があります。もちろん、持つことができる文字列の潜在的な値を列挙内の値に制限します。これは、必要な場合とそうでない場合があります。

もちろん、列挙には「その他」のエントリと fromString(String) メソッドを含めることができます。

ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
   case MILK: lap(); break;
   case WATER: sip(); break;
   case BEER: quaff(); break;
   case OTHER: 
   default: dance(); break;
}
于 2008-12-03T18:44:19.753 に答える
91

以下は、JeeBee の投稿に基づく完全な例で、カスタム メソッドを使用する代わりに Java 列挙型を使用しています。

Java SE 7 以降では、代わりに switch ステートメントの式で String オブジェクトを使用できることに注意してください。

public class Main {

    /**
    * @param args the command line arguments
    */
    public static void main(String[] args) {

      String current = args[0];
      Days currentDay = Days.valueOf(current.toUpperCase());

      switch (currentDay) {
          case MONDAY:
          case TUESDAY:
          case WEDNESDAY:
              System.out.println("boring");
              break;
          case THURSDAY:
              System.out.println("getting better");
          case FRIDAY:
          case SATURDAY:
          case SUNDAY:
              System.out.println("much better");
              break;

      }
  }

  public enum Days {

    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
  }
}
于 2011-09-16T13:11:33.573 に答える
26

整数に基づくスイッチは、非常に効率的なコードに最適化できます。他のデータ型に基づくスイッチは、一連の if() ステートメントにのみコンパイルできます。

そのため、C および C++ では整数型のスイッチのみが許可されます。他の型では意味がないためです。

C# の設計者は、利点がなくてもスタイルが重要であると判断しました。

Java の設計者は、明らかに C の設計者と同じように考えていました。

于 2008-12-03T18:32:10.383 に答える
19

James Curran は簡潔に次のように述べています。他のタイプでは無意味だったから」

私の意見は、ただそれだけですが、非プリミティブに切り替え始めるとすぐに、「等しい」と「==」について考え始める必要があるということです。まず、2 つの文字列を比較するのは、かなり時間のかかる手順になる可能性があり、前述のパフォーマンスの問題が追加されます。第二に、文字列を切り替える場合、大文字と小文字を無視して文字列を切り替える、ロケールを考慮/無視して文字列を切り替える、正規表現に基づいて文字列を切り替えるなどの要求があります。プログラマーのわずかな時間を犠牲にして言語開発者。

于 2008-12-03T20:49:42.213 に答える
12

上記の正当な議論に加えて、今日の多くの人々はswitch、Java の過去の手続き型 (C 時代に戻る) の残りの部分が時代遅れになっていると見なしていることを付け加えておきます。

私はこの意見を完全に共有しているわけではありません.switch少なくともその速度のために、場合によっては有用であると思います.とにかくelse if、いくつかのコードで見た一連のカスケード数値よりも優れています...

しかし実際には、スイッチが必要なケースを調べて、それを別の OO に置き換えることができないかどうかを確認する価値があります。たとえば、Java 1.5+ の列挙型、おそらく HashTable やその他のコレクション (スイッチを持たない Lua や JavaScript のように、(匿名の) 関数をファースト クラス シチズンとして持っていないことを後悔することがあります)、あるいはポリモーフィズムですらあります。

于 2008-12-03T21:45:38.480 に答える
8

JDK7以降を使用していない場合は、それhashCode()をシミュレートするために使用できます。String.hashCode()通常、異なる文字列に対しては異なる値を返し、等しい文字列に対しては常に等しい値を返すため、かなり信頼性が高いです (異なる文字列は、コメントで言及されている @Lii と同じハッシュ コードを生成でき"FB"ます。たとえば、 and "Ea")ドキュメントを参照してください。

したがって、コードは次のようになります。

String s = "<Your String>";

switch(s.hashCode()) {
case "Hello".hashCode(): break;
case "Goodbye".hashCode(): break;
}

そうすれば、技術的にはint.

または、次のコードを使用することもできます。

public final class Switch<T> {
    private final HashMap<T, Runnable> cases = new HashMap<T, Runnable>(0);

    public void addCase(T object, Runnable action) {
        this.cases.put(object, action);
    }

    public void SWITCH(T object) {
        for (T t : this.cases.keySet()) {
            if (object.equals(t)) { // This means that the class works with any object!
                this.cases.get(t).run();
                break;
            }
        }
    }
}
于 2015-05-23T20:16:28.527 に答える
4

他の回答では、これは Java 7 で追加され、以前のバージョンの回避策が提供されたと述べています。この答えは、「なぜ」に答えようとします

Java は、C++ の過度の複雑さに対する反応でした。シンプルでクリーンな言語になるように設計されています。

文字列は、言語で特殊なケースを少し処理しましたが、設計者が特殊なケースとシンタックス シュガーの量を最小限に抑えようとしていたことは明らかです。

文字列は単純なプリミティブ型ではないため、文字列の切り替えは内部ではかなり複雑です。これは、Java が設計された時点では一般的な機能ではなく、ミニマリストの設計にはうまく適合しませんでした。特に、彼らは文字列に対して == を特殊なケースにしないことに決めたので、== が機能しない場合にケースが機能するのは少し奇妙です (そしてそうです)。

1.0 から 1.4 の間、言語自体はほぼ同じままでした。Java の機能強化のほとんどは、ライブラリ側にありました。

Java 5 ですべてが変わり、言語が大幅に拡張されました。バージョン 7 と 8 では、さらに拡張が行われました。この態度の変化は、C# の台頭によって促進されたと思います。

于 2017-01-19T06:09:48.553 に答える
-2

あまりきれいではありませんが、Java 6以降の別の方法を次に示します。

String runFct = 
        queryType.equals("eq") ? "method1":
        queryType.equals("L_L")? "method2":
        queryType.equals("L_R")? "method3":
        queryType.equals("L_LR")? "method4":
            "method5";
Method m = this.getClass().getMethod(runFct);
m.invoke(this);
于 2017-06-01T06:33:28.257 に答える