Pertanyaan Mengapa saya tidak bisa menggunakan pernyataan switch pada String?


Apakah fungsi ini akan dimasukkan ke versi Java nanti?

Dapatkah seseorang menjelaskan mengapa saya tidak bisa melakukan ini, seperti dalam, cara teknis Jawa switch pernyataan bekerja?


931
2017-12-03 18:23


asal


Jawaban:


Tukar pernyataan dengan String kasus telah diimplementasikan di Java SE 7, setidaknya 16 tahun setelah mereka diminta pertama kali. Alasan yang jelas untuk penundaan itu tidak diberikan, tetapi kemungkinan itu berkaitan dengan kinerja.

Implementasi di JDK 7

Fitur ini sekarang telah diterapkan di javac  dengan proses "de-sugaring"; sintaks tingkat tinggi yang bersih menggunakan String konstanta dalam case deklarasi diperluas pada waktu kompilasi menjadi kode yang lebih kompleks mengikuti suatu pola. Kode yang dihasilkan menggunakan instruksi JVM yang selalu ada.

SEBUAH switch dengan String kasus diterjemahkan ke dalam dua switch selama kompilasi. Yang pertama memetakan setiap string ke integer unik-posisinya di switch asli. Ini dilakukan dengan beralih pertama pada kode hash dari label. Kasus yang sesuai adalah sebuah if pernyataan yang menguji persamaan string; jika ada tabrakan pada hash, tesnya adalah cascading if-else-if. Switch kedua mencerminkan kode sumber asli, tetapi mengganti label kasus dengan posisi yang sesuai. Proses dua langkah ini memudahkan untuk mempertahankan kontrol aliran dari switch asli.

Beralih di JVM

Untuk kedalaman yang lebih teknis switch, Anda dapat merujuk ke Spesifikasi JVM, di mana kompilasi pernyataan switch dijelaskan. Singkatnya, ada dua instruksi JVM berbeda yang dapat digunakan untuk switch, tergantung pada sparsity konstanta yang digunakan oleh kasus. Keduanya bergantung pada penggunaan konstanta integer untuk setiap kasus untuk dieksekusi secara efisien.

Jika konstanta padat, mereka digunakan sebagai indeks (setelah dikurangi nilai terendah) ke dalam tabel petunjuk pointer-the tableswitch petunjuk.

Jika konstantanya jarang, pencarian biner untuk kasus yang benar dilakukan — the lookupswitch petunjuk.

Dalam de-sugaring a switch di String objek, kedua instruksi kemungkinan akan digunakan. Itu lookupswitch cocok untuk switch pertama pada kode hash untuk menemukan posisi asli dari case. Hasil ordinal adalah fit alami untuk a tableswitch.

Kedua instruksi membutuhkan konstanta integer yang ditetapkan untuk setiap kasus yang akan diurutkan pada waktu kompilasi. Saat runtime, sedangkan O(1) penampilan dari tableswitch umumnya tampak lebih baik daripada O(log(n)) penampilan dari lookupswitchDiperlukan beberapa analisis untuk menentukan apakah tabel cukup padat untuk membenarkan tradeoff ruang-waktu. Bill Venners menulis artikel yang bagus yang mencakup ini secara lebih rinci, bersama dengan tampilan di bawah kap pada instruksi kontrol aliran Java lainnya.

Sebelum JDK 7

Sebelum JDK 7, enum bisa mendekati a Stringsaklar berbasis. Ini menggunakan statis valueOf metode yang dihasilkan oleh kompilator pada setiap enummengetik. Sebagai contoh:

Pill p = Pill.valueOf(str);
switch(p) {
  case RED:  pop();  break;
  case BLUE: push(); break;
}

961
2017-12-03 18:30



Jika Anda memiliki tempat di kode Anda di mana Anda dapat mengaktifkan String, maka mungkin lebih baik untuk refactor String menjadi penghitungan nilai yang mungkin, yang dapat Anda aktifkan. Tentu saja, Anda membatasi nilai-nilai potensial String yang dapat Anda miliki untuk mereka dalam pencacahan, yang mungkin atau mungkin tidak diinginkan.

Tentu saja pencacahan Anda bisa memiliki entri untuk 'yang lain', dan metode fromString (String), maka Anda dapat memiliki

ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
   case MILK: lap(); break;
   case WATER: sip(); break;
   case BEER: quaff(); break;
   case OTHER: 
   default: dance(); break;
}

120
2017-12-03 18:44



Berikut ini adalah contoh lengkap berdasarkan posting JeeBee, menggunakan java enum daripada menggunakan metode kustom.

Perhatikan bahwa di Java SE 7 dan yang lebih baru Anda dapat menggunakan objek String dalam ekspresi pernyataan switch sebagai gantinya.

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
  }
}

90
2017-09-16 13:11



Switch berdasarkan bilangan bulat dapat dioptimalkan untuk kode yang sangat efisien. Switch berdasarkan tipe data lain hanya dapat dikompilasi ke rangkaian pernyataan if ().

Karena alasan itu, C & C ++ hanya mengizinkan switch pada tipe integer, karena tidak ada gunanya dengan tipe lain.

Para perancang C # memutuskan bahwa gaya itu penting, bahkan jika tidak ada keuntungan.

Para desainer Jawa rupanya berpikir seperti para perancang C.


26
2017-12-03 18:32



James Curran dengan ringkas mengatakan: "Switch berdasarkan integer dapat dioptimalkan untuk kode yang sangat efisien. Switch berdasarkan tipe data lain hanya dapat dikompilasi ke rangkaian pernyataan if (). Karena alasan itu, C & C ++ hanya mengizinkan switch pada tipe integer, karena tidak ada gunanya dengan tipe lain. "

Pendapat saya, dan hanya itu, adalah bahwa begitu Anda mulai beralih pada non-primitif Anda harus mulai berpikir tentang "setara" versus "==". Pertama membandingkan dua string bisa menjadi prosedur yang cukup panjang, menambah masalah kinerja yang disebutkan di atas. Kedua, jika ada pengalihan string, akan ada permintaan untuk beralih pada string mengabaikan kasus, beralih pada string mempertimbangkan / mengabaikan lokal, beralih pada string berdasarkan regex .... Saya akan menyetujui keputusan yang menyelamatkan banyak waktu untuk pengembang bahasa dengan biaya sejumlah kecil waktu untuk programmer.


18
2017-12-03 20:49



Contoh langsung String penggunaan sejak 1.7 dapat ditampilkan juga:

public static void main(String[] args) {

    switch (args[0]) {
        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;
    }

}

18
2018-04-09 06:30



Selain argumen-argumen bagus di atas, saya akan menambahkan banyak orang hari ini melihat switch sebagai sisa usang dari masa lalu prosedural Jawa (kembali ke C kali).

Saya tidak sepenuhnya berbagi pendapat ini, saya pikir switch dapat memiliki kegunaannya dalam beberapa kasus, setidaknya karena kecepatannya, dan lagi pula itu lebih baik daripada beberapa seri cascading numerik else if Saya melihat dalam beberapa kode ...

Tapi memang, ada baiknya melihat kasus di mana Anda perlu beralih, dan melihat apakah itu tidak dapat digantikan oleh sesuatu yang lebih OO. Misalnya enums di Java 1.5+, mungkin HashTable atau beberapa koleksi lainnya (kadang saya menyesal tidak memiliki fungsi (anonim) sebagai warga kelas satu, seperti di Lua - yang tidak memiliki tombol - atau JavaScript) atau bahkan polimorfisme.


12
2017-12-03 21:45



Jika Anda tidak menggunakan JDK7 atau lebih tinggi, Anda dapat menggunakan hashCode() untuk mensimulasikannya. Karena String.hashCode() biasanya mengembalikan nilai yang berbeda untuk string yang berbeda dan selalu mengembalikan nilai yang sama untuk string yang sama, itu cukup dapat diandalkan (string yang berbeda bisa menghasilkan kode hash yang sama dengan @Lii yang disebutkan dalam komentar, seperti "FB" dan "Ea") Lihat dokumentasi.

Jadi, kode itu akan terlihat seperti ini:

String s = "<Your String>";

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

Dengan begitu, Anda secara teknis menyalakan int.

Atau, Anda bisa menggunakan kode berikut:

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;
            }
        }
    }
}

8
2018-05-23 20:16