Dlaczego nie mogę użyć polecenia switch na łańcuchu?

Czy ta funkcjonalność zostanie wprowadzona do późniejszej wersji Javy?

Czy ktoś może wyjaśnić, dlaczego nie mogę tego zrobić, czyli techniczne działanie instrukcji switch Javy?

Author: Mahozad, 2008-12-03

16 answers

Instrukcje Switch z przypadkami String zostały zaimplementowane wJava SE 7 , co najmniej 16 lat od ich pierwszego żądania. Nie podano wyraźnego powodu opóźnienia, ale prawdopodobnie miało to związek z wydajnością.

Implementacja w JDK 7

Funkcja została zaimplementowana wjavac dzięki procesowi" odcudzania"; czysta, wysokopoziomowa składnia wykorzystująca stałe String w deklaracjach case jest rozszerzana w czasie kompilacji do bardziej złożonych kod zgodny ze wzorem. Wynikowy kod wykorzystuje instrukcje JVM, które zawsze istniały.

A switch z String przypadki są tłumaczone na dwa przełączniki podczas kompilacji. Pierwszy mapuje każdy ciąg do unikalnej liczby całkowitej - jego położenie w oryginalnym przełączniku. Odbywa się to poprzez Pierwsze włączenie kodu skrótu etykiety. Odpowiedni przypadek to twierdzenie if, które testuje równość ciągu znaków; jeśli na hashu występują kolizje, test jest kaskadowy if-else-if. Drugi przełącznik lusterek to w oryginalnym kodzie źródłowym, ale zastępuje etykiety przypadków odpowiadającymi im pozycjami. Ten dwustopniowy proces ułatwia zachowanie kontroli przepływu oryginalnego przełącznika.

Przełączniki w JVM

Więcej szczegółów technicznych na temat switchmożna znaleźć w specyfikacji JVM, gdzie opisano kompilację instrukcji switch. W skrócie, istnieją dwie różne instrukcje JVM, które mogą być używane do przełącznika, w zależności od sparsity stałe używane przez przypadki. Oba zależą od użycia stałych całkowitych dla każdego przypadku, aby wykonać efektywnie.

Jeśli stałe są gęste, są używane jako indeks (po odjęciu najniższej wartości) do tabeli wskaźników instrukcji-instrukcji tableswitch.

Jeśli stałe są rzadkie, wykonywane jest binarne wyszukiwanie właściwego przypadku-instrukcja lookupswitch.

W dekodowaniu a switch na obiektach String, obie instrukcje mogą być użyte. Na lookupswitch nadaje się do pierwszego włączenia kodów skrótu, aby znaleźć oryginalną pozycję obudowy. Wynik porządkowy jest naturalnym dopasowaniem do tableswitch.

Obie instrukcje wymagają, aby stałe całkowite przypisane do każdego przypadku były sortowane w czasie kompilacji. Podczas wykonywania, podczas gdy wydajność O(1) tableswitch ogólnie wydaje się lepsza niż wydajność O(log(n)) lookupswitch, wymaga to pewnej analizy w celu określenia, czy tabela jest wystarczająco gęsta, aby uzasadnić kompromis czasoprzestrzenny. Bill Venners napisałświetny artykuł , który opisuje to bardziej szczegółowo, wraz z zerknięciem pod maską na inne instrukcje sterowania przepływem Javy.

Przed JDK 7

Przed JDK 7, enum może przybliżać przełącznik oparty na String. To wykorzystuje statyczne valueOf metoda generowana przez kompilator dla każdego typu enum. Na przykład:

Pill p = Pill.valueOf(str);
switch(p) {
  case RED:  pop();  break;
  case BLUE: push(); break;
}
 1011
Author: erickson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-13 19:02:04

Jeśli masz miejsce w kodzie, w którym możesz włączyć Łańcuch znaków, to może być lepiej, aby refaktor był wyliczeniem możliwych wartości, które możesz włączyć. Oczywiście, możesz ograniczyć potencjalne wartości ciągów, które możesz mieć do tych w wyliczeniu, które mogą lub nie mogą być pożądane.

Oczywiście twoje wyliczenie może mieć wpis Dla 'other' i metodę Fromstring (String), wtedy możesz mieć

ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
   case MILK: lap(); break;
   case WATER: sip(); break;
   case BEER: quaff(); break;
   case OTHER: 
   default: dance(); break;
}
 127
Author: JeeBee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-03 18:44:19

Poniżej znajduje się kompletny przykład oparty na poście JeeBee, używając Java enum zamiast niestandardowej metody.

Zauważ, że w Java SE 7 i nowszych można użyć obiektu String w wyrażeniu instrukcji switch.

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
  }
}
 91
Author: Thulani Chivandikwa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-19 19:09:35

Przełączniki oparte na liczbach całkowitych mogą być zoptymalizowane do bardzo efektywnego kodu. Przełączniki bazujące na innym typie danych mogą być kompilowane tylko do serii poleceń if ().

Z tego powodu C & C++ zezwala tylko na przełączanie na typy całkowite, ponieważ było to bezcelowe w przypadku innych typów.

Projektanci C# uznali, że styl jest ważny, nawet jeśli nie ma żadnej przewagi.

Projektanci Javy najwyraźniej myśleli jak projektanci C.

 26
Author: James Curran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-03 18:32:10

James Curran zwięźle mówi: "Przełączniki oparte na liczbach całkowitych mogą być zoptymalizowane do bardzo skutecznego kodu. Przełączniki bazujące na innym typie danych mogą być kompilowane tylko do serii poleceń if (). Z tego powodu C & C++ zezwala tylko na przełączanie na typy całkowite, ponieważ było to bezcelowe w przypadku innych typów."

Moim zdaniem, i tylko tyle, że jak tylko zaczniesz włączać nie-prymitywy, musisz zacząć myśleć o "równych " kontra"==". Po pierwsze porównywanie dwóch ciągów może być dość długa procedura, dodając do problemów z wydajnością, które są wymienione powyżej. Po drugie, jeśli jest włączanie łańcuchów, będzie zapotrzebowanie na włączanie łańcuchów ignorując wielkość liter, włączanie łańcuchów uwzględniając / ignorując ustawienia regionalne,włączanie łańcuchów na podstawie wyrażeń regularnych.... Chciałbym zatwierdzić decyzję, która zaoszczędziła dużo czasu dla programistów języka kosztem niewielkiej ilości czasu dla programistów.

 19
Author: DJClayworth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-03 20:49:42

Przykład bezpośredniego użycia String od wersji 1.7 może być również pokazany:

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

}
 19
Author: Gunnar Forsgren - Mobimation,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-06 14:32:55

Oprócz powyższych dobrych argumentów dodam, że wiele osób dzisiaj postrzega switch jako przestarzałą pozostałość proceduralnej przeszłości Javy(z powrotem do czasów C).

Nie do końca podzielam tę opinię, myślę, że switch może mieć swoją przydatność w niektórych przypadkach, przynajmniej ze względu na szybkość, a w każdym razie jest lepsza niż jakaś seria kaskadowych liczb else if widziałem w jakimś kodzie...

Ale rzeczywiście, warto przyjrzeć się sprawie, w której potrzebujesz przełącznika, i zobaczyć, czy nie można go zastąpić przez coś więcej. Na przykład enums w Javie 1.5+, może HashTable lub jakiś inny zbiór (czasami żałuję, że nie mamy (anonimowych) funkcji jako obywatel pierwszej klasy, jak w Lua - który nie ma switch - lub JavaScript) lub nawet polimorfizm.

 12
Author: PhiLho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-03 21:45:38

Jeśli nie używasz JDK7 lub wyższego, możesz użyć hashCode() do jego symulacji. Ponieważ String.hashCode()zwykle zwraca różne wartości dla różnych łańcuchów i zawsze zwraca równe wartości dla równych łańcuchów, jest to dość niezawodne (różne łańcuchy mogą wygenerować ten sam kod skrótu, co @Lii wspomniany w komentarzu, na przykład "FB" i "Ea") Zobacz Dokumentacja.

Więc kod będzie wyglądał tak:

String s = "<Your String>";

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

W ten sposób, technicznie przełączasz się na int.

Alternatywnie możesz użyć następującego kodu:

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
Author: HyperNeutrino,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-26 03:14:43

Od lat używamy do tego preprocesora (n open source).

//#switch(target)
case "foo": code;
//#end

Wstępnie przetworzone pliki noszą nazwę Foo.jpp i przetworzyć na Foo.java ze skryptem ant.

Zaletą jest to, że jest on przetwarzany do Javy, która działa na 1.0 (chociaż zazwyczaj obsługiwaliśmy tylko z powrotem do 1.4). Również było to znacznie łatwiejsze do zrobienia (wiele przełączników ciągów) w porównaniu do fudging go z enums lub innych obejść-kod był o wiele łatwiejszy do odczytania, utrzymania i zrozumienia. IIRC (nie można zapewnić statystyki lub rozumowanie techniczne w tym momencie) był również szybszy niż naturalne odpowiedniki Javy.

Wady Są to, że nie edytujesz Javy, więc jest to trochę więcej workflow (edycja, proces, kompilacja/test) plus IDE odsyła do Javy, która jest trochę zawiła (przełącznik staje się serią kroków logicznych if/else) i kolejność przypadków przełącznika nie jest zachowana.

Nie polecam go dla 1.7+ , ale jest przydatny, jeśli chcesz zaprogramować Javę, która obsługuje wcześniejsze JVMs (ponieważ Joe public rzadko ma najnowszą zainstalowaną).

Możesz go pobrać Z SVN lub przejrzeć kod online . Musisz EBuild , aby zbudować go tak, jak jest.

 4
Author: Charles Goodwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-02 14:38:26

Inne odpowiedzi mówiły, że dodano to w Javie 7 i podano obejścia dla wcześniejszych wersji. Ta odpowiedź próbuje odpowiedzieć "dlaczego"

Java była reakcją na nadmierną złożoność C++. Został zaprojektowany, aby być prostym, czystym językiem.

String ma trochę specjalnej obsługi przypadków w języku, ale wydaje mi się jasne, że projektanci starali się utrzymać ilość specjalnej obudowy i cukru składniowego do minimum.

Włączanie ciągów jest dość złożone pod maską, ponieważ struny nie są prostymi prymitywnymi typami. Nie była to wspólna cecha w czasie projektowania Java i tak naprawdę nie pasuje do minimalistycznego projektu. Szczególnie, że nie zdecydowali się na specjalne case == dla ciągów, byłoby (i jest) trochę dziwne, aby case działało tam, gdzie = = nie.

Między 1.0 A 1.4 sam język pozostał prawie taki sam. Większość ulepszeń Javy była po stronie biblioteki.

To wszystko zmieniło się z Java 5, język został znacznie rozszerzony. Kolejne rozszerzenia pojawiły się w wersjach 7 i 8. Spodziewam się, że ta zmiana nastawienia była napędzana wzrostem C #

 4
Author: plugwash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-19 06:09:48

JEP 354: Switch Expressions (Preview) w JDK-13 i JEP 361: Switch Expressions (Standard) w JDK - 14 rozszerzy switch statement więc może być używany jako wyrażenie.

Teraz możesz:

  • bezpośrednio Przypisz zmienną z Przełącz wyrażenie,
  • użyj nowej formy switch label (case L ->):

    Kod po prawej stronie etykiety przełącznika "case L - >" jest ograniczony do być wyrażeniem, blokiem lub (dla wygody) instrukcją throw.

  • użyj wielu stałych na każdy przypadek, oddzielonych przecinkami,
  • a także nie ma już wartości :

    Aby uzyskać wartość z wyrażenia switch, Instrukcja break with value jest odrzucana na rzecz instrukcji yield.

Więc demo z odpowiedzi (1, 2) może wyglądać tak:

  public static void main(String[] args) {
    switch (args[0]) {
      case "Monday", "Tuesday", "Wednesday" ->  System.out.println("boring");
      case "Thursday" -> System.out.println("getting better");
      case "Friday", "Saturday", "Sunday" -> System.out.println("much better");
    }
 0
Author: Iskuskov Alexander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-09 10:39:17

W Javie 11+ jest to możliwe również ze zmiennymi. Jedynym warunkiem jest to, że musi być stała.

Na Przykład:

final String LEFT = "left";
final String RIGHT = "right";
final String UP = "up";
final String DOWN = "down";

String var = ...;

switch (var) {
    case LEFT:
    case RIGHT:
    case DOWN:
    default:
        return 0;
}

PS. Nie próbowałem tego wcześniej z JDK. Więc zaktualizuj odpowiedź, jeśli jest tam obsługiwana.

 0
Author: Imtiaz Shakil Siddique,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-30 15:35:58

Niezbyt ładna, ale tutaj jest inny sposób na Javę 6 i poniżej:

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);
 -2
Author: Conete Cristian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-01 06:33:28

W Groovy jar osadzam GROOVY jar i tworzę groovy klasę użytkową, aby robić te wszystkie rzeczy i nie tylko, co uważam za irytujące w Javie (ponieważ utknąłem przy użyciu Javy 6 w przedsiębiorstwie.)

it.'p'.each{
switch ([email protected]()){
   case "choclate":
     myholder.myval=(it.text());
     break;
     }}...
 -4
Author: Alex Punnen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-15 01:40:54

Kiedy używasz intellij spójrz również na:

Plik - > Struktura Projektu - > Projekt

Plik - > Struktura Projektu - > Moduły

Jeśli masz wiele modułów, upewnij się, że ustawiłeś odpowiedni poziom języka w zakładce moduł.

 -4
Author: botenvouwer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-06 12:13:26
public class StringSwitchCase { 

    public static void main(String args[]) {

        visitIsland("Santorini"); 
        visitIsland("Crete"); 
        visitIsland("Paros"); 

    } 

    public static void visitIsland(String island) {
         switch(island) {
          case "Corfu": 
               System.out.println("User wants to visit Corfu");
               break; 
          case "Crete": 
               System.out.println("User wants to visit Crete");
               break; 
          case "Santorini": 
               System.out.println("User wants to visit Santorini");
               break; 
          case "Mykonos": 
               System.out.println("User wants to visit Mykonos");
               break; 
         default: 
               System.out.println("Unknown Island");
               break; 
         } 
    } 

} 
 -9
Author: Issac Balaji,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-29 20:27:23