Development Tip

문자열에서 switch 문을 사용할 수없는 이유는 무엇입니까?

yourdevel 2020. 9. 27. 14:11
반응형

문자열에서 switch 문을 사용할 수없는 이유는 무엇입니까?


이 기능이 최신 Java 버전에 포함될 예정입니까?

누군가 내가 왜 이것을 할 수 없는지 설명 할 수 있습니까 switch?


String케이스가 포함 된 Switch 문 은 처음 요청 된 지 최소 16 년이 지난 Java SE 7 에서 구현되었습니다 . 지연에 대한 명확한 이유는 제공되지 않았지만 성능과 관련이있을 수 있습니다.

JDK 7에서 구현

이 기능은 이제 javac "설탕 제거"프로세스 로 구현되었습니다 . 선언 String에서 상수를 사용하는 깔끔하고 높은 수준의 구문 case은 컴파일 타임에 패턴을 따르는 더 복잡한 코드로 확장됩니다. 결과 코드는 항상 존재했던 JVM 명령어를 사용합니다.

switch을 가진 String경우는 컴파일하는 동안 두 개의 스위치로 변환됩니다. 첫 번째는 각 문자열을 고유 한 정수 (원래 스위치에서의 위치)에 매핑합니다. 이것은 먼저 레이블의 해시 코드를 켜서 수행됩니다. 해당 케이스는 if문자열이 같은지 테스트 하는 문입니다. 해시에 충돌이있는 경우 테스트는 계단식 if-else-if. 두 번째 스위치는 원본 소스 코드에서이를 반영하지만 케이스 레이블을 해당 위치로 대체합니다. 이 2 단계 프로세스를 통해 원래 스위치의 흐름 제어를 쉽게 유지할 수 있습니다.

JVM의 스위치

에 대한 자세한 기술적 인 내용스위치 문 컴파일 이 설명 된 switchJVM 사양을 참조 할 수 있습니다 . 요컨대, 케이스에서 사용하는 상수의 희소성에 따라 스위치에 사용할 수있는 두 가지 JVM 명령어가 있습니다. 둘 다 효율적으로 실행하기 위해 각 경우에 정수 상수 사용에 의존합니다.

상수가 조밀하면 명령어 포인터 테이블 (명령어)에 대한 인덱스 (가장 낮은 값을 뺀 후)로 tableswitch사용됩니다.

상수가 희소 한 경우 올바른 케이스 ( lookupswitch명령어)에 대한 이진 검색이 수행 됩니다.

개체에서 설탕 switch을 제거 String할 때 두 가지 지침이 모두 사용됩니다. lookupswitch해시 코드에 제 1 스위치 케이스의 원래 위치를 찾기 위해 적합하다. 결과 서수는 tableswitch.

두 명령어 모두 각 케이스에 할당 된 정수 상수를 컴파일 타임에 정렬해야합니다. 그동안 런타임시 O(1)의 성능 tableswitch일반적으로이보다 더 나은 표시 O(log(n))의 성능 lookupswitch, 그것은 테이블이 공간 - 시간의 균형을 정당화하기 조밀 충분히인지 여부를 결정하기 위해 몇 가지 분석이 필요합니다. Bill Venners는 다른 Java 흐름 제어 지침에 대한 내부적 인 내용과 함께이를 자세히 다루는 훌륭한 기사작성 했습니다 .

JDK 7 이전

JDK 7 이전에는 기반 스위치 enumString비슷할 수있었습니다 . 이것은 모든 유형 에서 컴파일러에 의해 생성 된 정적valueOf 메서드를 사용 합니다enum . 예를 들면 :

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

코드에 문자열을 켤 수있는 위치가있는 경우, 가능한 값의 열거가되도록 문자열을 리팩토링하는 것이 더 좋을 수 있습니다. 물론, 당신이 가질 수있는 문자열의 잠재적 인 값을 열거 형의 값으로 제한합니다.

물론 열거 형에는 '기타'항목과 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;
}

다음은 JeeBee의 포스트를 바탕으로 커스텀 메소드를 사용하는 대신 자바 enum을 사용한 완전한 예제입니다.

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

Switches based on integers can be optimized to very efficent code. Switches based on other data type can only be compiled to a series of if() statements.

For that reason C & C++ only allow switches on integer types, since it was pointless with other types.

The designers of C# decided that the style was important, even if there was no advantage.

The designers of Java apparently thought like the designers of C.


James Curran succinctly says: "Switches based on integers can be optimized to very efficent code. Switches based on other data type can only be compiled to a series of if() statements. For that reason C & C++ only allow switches on integer types, since it was pointless with other types."

My opinion, and it's only that, is that as soon as you start switching on non-primitives you need to start thinking about "equals" versus "==". Firstly comparing two strings can be a fairly lengthy procedure, adding to the performance problems that are mentioned above. Secondly if there is switching on strings there will be demand for switching on strings ignoring case, switching on strings considering/ignoring locale,switching on strings based on regex.... I would approve of a decision that saved a lot of time for the language developers at the cost of a small amount of time for programmers.


An example of direct String usage since 1.7 may be shown as well:

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

}

Beside the above good arguments, I will add that lot of people today see switch as an obsolete remainder of procedural past of Java (back to C times).

I don't fully share this opinion, I think switch can have its usefulness in some cases, at least because of its speed, and anyway it is better than some series of cascading numerical else if I saw in some code...

But indeed, it is worth looking at the case where you need a switch, and see if it cannot be replaced by something more OO. For example enums in Java 1.5+, perhaps HashTable or some other collection (sometime I regret we don't have (anonymous) functions as first class citizen, as in Lua — which doesn't have switch — or JavaScript) or even polymorphism.


If you are not using JDK7 or higher, you can use hashCode() to simulate it. Because String.hashCode() usually returns different values for different strings and always returns equal values for equal strings, it is fairly reliable (Different strings can produce the same hash code as @Lii mentioned in a comment, such as "FB" and "Ea") See documentation.

So, the code would look like this:

String s = "<Your String>";

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

That way, you are technically switching on an int.

Alternatively, you could use the following code:

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

For years we've been using a(n open source) preprocessor for this.

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

Preprocessed files are named Foo.jpp and get processed into Foo.java with an ant script.

Advantage is it is processed into Java that runs on 1.0 (although typically we only supported back to 1.4). Also it was far easier to do this (lots of string switches) compared to fudging it with enums or other workarounds - code was a lot easier to read, maintain, and understand. IIRC (can't provide statistics or technical reasoning at this point) it was also faster than the natural Java equivalents.

Disadvantages are you aren't editing Java so it's a bit more workflow (edit, process, compile/test) plus an IDE will link back to the Java which is a little convoluted (the switch becomes a series of if/else logic steps) and the switch case order is not maintained.

I wouldn't recommend it for 1.7+ but it's useful if you want to program Java that targets earlier JVMs (since Joe public rarely has the latest installed).

You can get it from SVN or browse the code online. You'll need EBuild to build it as-is.


Other answers have said this was added in Java 7 and given workarounds for earlier versions. This answer tries to answer the "why"

Java was a reaction to the over-complexities of C++. It was designed to be a simple clean language.

String got a little bit of special case handling in the language but it seems clear to me that the designers were trying to keep the amount of special casing and syntactic sugar to a minimum.

switching on strings is fairly complex under the hood since strings are not simple primitive types. It was not a common feature at the time Java was designed and doesn't really fit in well with the minimalist design. Especially as they had decided not to special case == for strings, it would be (and is) a bit strange for case to work where == doesn't.

Between 1.0 and 1.4 the language itself stayed pretty much the same. Most of the enhancements to Java were on the library side.

That all changed with Java 5, the language was substantially extended. Further extensions followed in versions 7 and 8. I expect that this change of attitude was driven by the rise of C#


Not very pretty, but here is another way for Java 6 and bellow:

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

It's a breeze in Groovy; I embed the groovy jar and create a groovy utility class to do all these things and more which I find exasperating to do in Java (since I am stuck using Java 6 in the enterprise.)

it.'p'.each{
switch (it.@name.text()){
   case "choclate":
     myholder.myval=(it.text());
     break;
     }}...

When you use intellij also look at:

File -> Project Structure -> Project

File -> Project Structure -> Modules

When you have multiple modules make sure you set the correct language level in the module tab.


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

} 

참고URL : https://stackoverflow.com/questions/338206/why-cant-i-use-switch-statement-on-a-string

반응형