이 기능이 이후의 Java 버전에 적용됩니까?
누군가 Java의 switch
명령문이 작동 하는 기술적 방식과 같이 왜 내가 이것을 할 수 없는지 설명 할 수 있습니까 ?
답변
String
케이스가있는 스위치 명령문 은 처음 요청한 후 16 년 이상 Java SE 7 에서 구현되었습니다 . 지연의 명확한 이유는 제공되지 않았지만 성능과 관련이있을 수 있습니다.
JDK 7 구현
이 기능은 이제 javac
“설탕 제거”프로세스 로 구현되었습니다 . 선언 String
에서 상수를 사용하는 깨끗하고 높은 수준의 구문 case
은 컴파일 타임에 패턴에 따라 더 복잡한 코드로 확장됩니다. 결과 코드는 항상 존재하는 JVM 명령어를 사용합니다.
switch
을 가진 String
경우는 컴파일하는 동안 두 개의 스위치로 변환됩니다. 첫 번째는 각 문자열을 고유 한 정수 (원래 스위치의 위치)에 매핑합니다. 레이블의 해시 코드를 먼저 켜면됩니다. 해당하는 경우는 if
문자열 동등성을 테스트 하는 명령문입니다. 해시에 충돌이 있으면 테스트는 계단식 if-else-if
입니다. 두 번째 스위치는 원본 소스 코드에서이를 미러링하지만 케이스 레이블을 해당 위치로 대체합니다. 이 2 단계 프로세스를 통해 원래 스위치의 흐름 제어를 쉽게 유지할 수 있습니다.
JVM에서 스위치
보다 자세한 기술 내용 은 스위치switch
사양의 컴파일 이 설명 되어있는 JVM 사양을 참조하십시오 . 간단히 말해, 사례에 사용되는 상수의 희소성에 따라 스위치에 사용할 수있는 두 가지 다른 JVM 명령어가 있습니다. 둘 다 각 경우에 정수 상수를 사용하여 효율적으로 실행합니다.
상수가 밀도가 높으면 명령 포인터 테이블 (명령)에 인덱스 (가장 낮은 값을 뺀 후)로 tableswitch
사용됩니다.
상수가 드문 경우 올바른 사례에 대한 이진 검색 ( lookupswitch
명령) 이 수행 됩니다.
switch
on String
객체의 설탕 을 제거 할 때 두 가지 지침이 모두 사용됩니다. 는 lookupswitch
해시 코드에 제 1 스위치 케이스의 원래 위치를 찾기 위해 적합하다. 결과 서수는 자연스럽게 적합합니다 tableswitch
.
두 명령어 모두 각 경우에 할당 된 정수 상수를 컴파일 타임에 정렬해야합니다. 그동안 런타임시 O(1)
의 성능 tableswitch
일반적으로이보다 더 나은 표시 O(log(n))
의 성능 lookupswitch
, 그것은 테이블이 공간 – 시간의 균형을 정당화하기 조밀 충분히인지 여부를 결정하기 위해 몇 가지 분석이 필요합니다. Bill Venners는 다른 Java 흐름 제어 명령에 대한 간단한 설명과 함께이 내용을 자세히 다루는 훌륭한 기사 를 작성 했습니다 .
JDK 7 이전
JDK 7 이전에는 기반 스위치와 enum
비슷했습니다 String
. 이것은 모든 유형 에서 컴파일러가 생성 한 정적valueOf
메소드를 사용 합니다enum
. 예를 들면 다음과 같습니다.
Pill p = Pill.valueOf(str);
switch(p) {
case RED: pop(); break;
case BLUE: push(); break;
}
답변
코드에서 문자열을 켤 수있는 장소가 있다면 가능한 값의 열거로 문자열을 리팩터링하는 것이 좋습니다. 물론, 열거 할 수있는 문자열의 잠재적 인 값을 제한 할 수 있습니다.
물론 열거에는 ‘other’에 대한 항목과 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;
}
답변
다음은 사용자 정의 메소드를 사용하는 대신 java enum을 사용하여 JeeBee의 게시물을 기반으로 한 완전한 예제입니다.
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
}
}
답변
정수 기반 스위치는 매우 효율적인 코드로 최적화 될 수 있습니다. 다른 데이터 유형을 기반으로하는 스위치는 일련의 if () 문으로 만 컴파일 할 수 있습니다.
이러한 이유로 C & C ++는 정수 유형에서만 스위치를 허용합니다. 다른 유형에서는 의미가 없기 때문입니다.
C #의 디자이너는 이점이 없더라도 스타일이 중요하다고 결정했습니다.
Java의 디자이너는 분명히 C의 디자이너처럼 생각했습니다.
답변
String
1.7 이후 의 직접 사용 예도 표시 될 수 있습니다.
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;
}
}
답변
James Curran은 간결하게 말합니다. “정수를 기반으로하는 스위치는 매우 효율적인 코드로 최적화 할 수 있습니다. 다른 데이터 유형을 기반으로하는 스위치는 일련의 if () 문으로 만 컴파일 할 수 있습니다. 따라서 C & C ++에서는 정수 유형에 대한 스위치 만 허용합니다. 다른 유형과는 무의미했기 때문입니다. “
저의 의견은 단지 프리미티브가 아닌 스위치를 시작하자마자 “같음”과 “==”에 대해 생각하기 시작해야한다는 것입니다. 먼저 두 문자열을 비교하는 것은 상당히 긴 절차 일 수 있으며 위에서 언급 한 성능 문제를 더합니다. 둘째로 문자열을 전환하는 경우 대소 문자를 무시하고 로케일을 고려하거나 무시하는 문자열을 전환하고 정규 표현식을 기반으로 문자열을 전환하는 문자열을 전환해야합니다 …. 나는 많은 시간을 절약 한 결정을 승인 할 것입니다. 프로그래머에게는 적은 시간이 소요되는 언어 개발자.
답변
위의 좋은 주장 외에도 오늘 많은 사람들이 switch
이 Java 과거의 절차 적 과거 (C 번으로 돌아가는)의 남은 부분으로 간주 .
나는이 견해를 완전히 공유하지 않으며 switch
, 적어도 속도 때문에 어떤 경우에는 유용 할 수 있다고 생각 합니다. 어쨌든 else if
일부 코드에서 본 일련의 계단식 숫자보다 낫습니다 …
그러나 실제로 스위치가 필요한 경우를 살펴보고 더 많은 OO로 바꿀 수 없는지 살펴볼 가치가 있습니다. 예를 들어 Java 1.5 이상의 열거 형, 아마도 HashTable 또는 다른 컬렉션 (때로는 스위치 또는 JavaScript가없는 Lua에서와 같이 일급 시민으로서 (익명) 기능이 없거나 유감스럽게 생각합니다) 또는 다형성이 있습니다.