이 기능이 이후의 Java 버전에 적용됩니까?
누군가 Java의 switch
명령문이 작동 하는 기술적 방식과 같이 왜 내가 이것을 할 수 없는지 설명 할 수 있습니까 ?
"Don't hold your breath."
lol, bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179
이 기능이 이후의 Java 버전에 적용됩니까?
누군가 Java의 switch
명령문이 작동 하는 기술적 방식과 같이 왜 내가 이것을 할 수 없는지 설명 할 수 있습니까 ?
"Don't hold your breath."
lol, bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179
답변:
String
케이스가있는 스위치 명령문 은 처음 요청한 후 16 년 이상 Java SE 7 에서 구현되었습니다 . 지연의 명확한 이유는 제공되지 않았지만 성능과 관련이있을 수 있습니다.
이 기능은 이제 javac
"설탕 제거"프로세스 로 구현되었습니다 . 선언 String
에서 상수를 사용하는 깨끗하고 높은 수준의 구문 case
은 컴파일 타임에 패턴에 따라 더 복잡한 코드로 확장됩니다. 결과 코드는 항상 존재하는 JVM 명령어를 사용합니다.
switch
을 가진 String
경우는 컴파일하는 동안 두 개의 스위치로 변환됩니다. 첫 번째는 각 문자열을 고유 한 정수 (원래 스위치의 위치)에 매핑합니다. 레이블의 해시 코드를 먼저 켜면됩니다. 해당하는 경우는 if
문자열 동등성을 테스트 하는 명령문입니다. 해시에 충돌이 있으면 테스트는 계단식 if-else-if
입니다. 두 번째 스위치는 원본 소스 코드에서이를 미러링하지만 케이스 레이블을 해당 위치로 대체합니다. 이 2 단계 프로세스를 통해 원래 스위치의 흐름 제어를 쉽게 유지할 수 있습니다.
보다 자세한 기술 내용 은 스위치switch
사양의 컴파일 이 설명 되어있는 JVM 사양을 참조하십시오 . 간단히 말해, 사례에 사용되는 상수의 희소성에 따라 스위치에 사용할 수있는 두 가지 다른 JVM 명령어가 있습니다. 둘 다 각 경우에 정수 상수를 사용하여 효율적으로 실행합니다.
상수가 밀도가 높으면 명령 포인터 테이블 (명령)에 인덱스 (가장 낮은 값을 뺀 후)로 tableswitch
사용됩니다.
상수가 드문 경우 올바른 사례에 대한 이진 검색 ( lookupswitch
명령) 이 수행 됩니다.
switch
on String
객체의 설탕 을 제거 할 때 두 가지 지침이 모두 사용됩니다. 는 lookupswitch
해시 코드에 제 1 스위치 케이스의 원래 위치를 찾기 위해 적합하다. 결과 서수는 자연스럽게 적합합니다 tableswitch
.
두 명령어 모두 각 경우에 할당 된 정수 상수를 컴파일 타임에 정렬해야합니다. 그동안 런타임시 O(1)
의 성능 tableswitch
일반적으로이보다 더 나은 표시 O(log(n))
의 성능 lookupswitch
, 그것은 테이블이 공간 - 시간의 균형을 정당화하기 조밀 충분히인지 여부를 결정하기 위해 몇 가지 분석이 필요합니다. Bill Venners는 다른 Java 흐름 제어 명령에 대한 간단한 설명과 함께이 내용을 자세히 다루는 훌륭한 기사 를 작성 했습니다 .
JDK 7 이전에는 기반 스위치와 enum
비슷했습니다 String
. 이것은 모든 유형 에서 컴파일러가 생성 한 정적valueOf
메소드를 사용 합니다enum
. 예를 들면 다음과 같습니다.
Pill p = Pill.valueOf(str);
switch(p) {
case RED: pop(); break;
case BLUE: push(); break;
}
Pill
에 따라 일부 작업을 수행하는 데 사용 하는 str
경우 str
예외를 잡거나 valueOf
수동으로 불필요한 오버 헤드를 추가하는 각 열거 유형 필자의 경험 valueOf
에 따르면 나중에 문자열 값의 형식 안전 표현이 필요한 경우 열거 형으로 변환하는 데 사용 하는 것이 합리적 입니다.
(hash >> x) & ((1<<y)-1)
모든 문자열 (또는 적어도 그보다 크지 않습니다). hashCode
(1<<y)
코드에서 문자열을 켤 수있는 장소가 있다면 가능한 값의 열거로 문자열을 리팩터링하는 것이 좋습니다. 물론, 열거 할 수있는 문자열의 잠재적 인 값을 제한 할 수 있습니다.
물론 열거에는 '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 ++에서는 정수 유형에 대한 스위치 만 허용합니다. 다른 유형과는 무의미했기 때문입니다. "
저의 의견은 단지 프리미티브가 아닌 스위치를 시작하자마자 "같음"과 "=="에 대해 생각하기 시작해야한다는 것입니다. 먼저 두 문자열을 비교하는 것은 상당히 긴 절차 일 수 있으며 위에서 언급 한 성능 문제를 더합니다. 둘째로 문자열을 전환하는 경우 대소 문자를 무시하고 로케일을 고려하거나 무시하는 문자열을 전환하고 정규 표현식을 기반으로 문자열을 전환하는 문자열을 전환해야합니다 .... 나는 많은 시간을 절약 한 결정을 승인 할 것입니다. 프로그래머에게는 적은 시간이 소요되는 언어 개발자.
matched
와 not matched
. ([named] groups / etc와 같은 것을 고려하지 않음.)
위의 좋은 주장 외에도 오늘 많은 사람들이 switch
이 Java 과거의 절차 적 과거 (C 번으로 돌아가는)의 남은 부분으로 간주 .
나는이 견해를 완전히 공유하지 않으며 switch
, 적어도 속도 때문에 어떤 경우에는 유용 할 수 있다고 생각 합니다. 어쨌든 else if
일부 코드에서 본 일련의 계단식 숫자보다 낫습니다 ...
그러나 실제로 스위치가 필요한 경우를 살펴보고 더 많은 OO로 바꿀 수 없는지 살펴볼 가치가 있습니다. 예를 들어 Java 1.5 이상의 열거 형, 아마도 HashTable 또는 다른 컬렉션 (때로는 스위치 또는 JavaScript가없는 Lua에서와 같이 일급 시민으로서 (익명) 기능이 없거나 유감스럽게 생각합니다) 또는 다형성이 있습니다.
JDK7 이상을 사용하지 않는 경우 hashCode()
이를 사용 하여 시뮬레이션 할 수 있습니다 . String.hashCode()
일반적으로 서로 다른 문자열에 대해 다른 값을 반환하고 항상 같은 문자열에 대해 동일한 값을 반환 하기 때문에 상당히 안정적입니다 (다른 문자열 은"FB"
and 등의 주석에 언급 된 @Lii와 동일한 해시 코드를 생성 할 수 있음"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;
}
}
}
}
case
명령문이 항상 일정한 값이어야한다고 생각한 것처럼 컴파일이 놀랍습니다 String.hashCode()
(실제로 계산이 JVM간에 변경되지 않은 경우에도).
case
명령문 값은 컴파일 타임에 결정할 수 없으므로 잘 작동합니다.
수년간 우리는 이것을 위해 (n 오픈 소스) 프리 프로세서를 사용해 왔습니다.
//#switch(target)
case "foo": code;
//#end
사전 처리 된 파일 이름은 Foo.jpp이며 ant 스크립트를 사용하여 Foo.java로 처리됩니다.
장점은 1.0에서 실행되는 Java로 처리된다는 점입니다 (일반적으로 1.4로만 지원됨). 또한 열거 형이나 다른 해결 방법으로 처리하는 것과 비교 하여이 작업을 수행하는 것이 훨씬 쉽습니다 (코드를 읽고 유지 관리하고 이해하는 것이 훨씬 쉬웠습니다. IIRC (현재 통계 또는 기술적 추론을 제공 할 수 없음)는 자연 Java에 비해 빠릅니다.
단점은 Java를 편집하지 않으므로 약간 더 많은 워크 플로우 (편집, 프로세스, 컴파일 / 테스트)와 IDE가 약간 복잡한 Java로 다시 링크됩니다 (스위치는 일련의 if / else 논리 단계가 됨) 스위치 케이스 순서는 유지되지 않습니다.
1.7 이상에서는 권장하지 않지만 이전 JVM을 대상으로하는 Java를 프로그래밍하려는 경우 유용합니다 (Joe public은 최신 버전을 거의 설치하지 않기 때문에).
SVN에서 가져 오거나 온라인으로 코드를 찾아 볼 수 있습니다 . 그대로 빌드 하려면 EBuild 가 필요합니다 .
다른 답변에 따르면 Java 7에 추가되었으며 이전 버전에 대한 해결 방법이 제공되었습니다. 이 답변은 "왜"
Java는 C ++의 복잡한 문제에 대한 반응이었습니다. 단순하고 깨끗한 언어로 설계되었습니다.
String은 언어에서 약간의 특수한 경우를 처리했지만 디자이너가 특별한 케이스와 구문 설탕의 양을 최소한으로 유지하려고 노력하고 있음이 분명합니다.
문자열은 단순한 기본 유형이 아니기 때문에 문자열 전환은 상당히 복잡합니다. Java가 디자인 될 당시에는 일반적인 기능이 아니 었으며 미니멀리스트 디자인에는 적합하지 않습니다. 특히 그들은 문자열에 대해 특별한 경우 ==를 사용하지 않기로 결정했기 때문에 ==가 아닌 경우 작동하는 것은 조금 이상합니다.
1.0과 1.4 사이에서 언어 자체는 거의 동일하게 유지되었습니다. Java에 대한 대부분의 개선 사항은 라이브러리 측에있었습니다.
모든 것이 Java 5로 바뀌면서 언어가 실질적으로 확장되었습니다. 버전 7과 8에서 추가 확장이 이어졌습니다. 이러한 태도 변화는 C #의 상승으로 인한 것 같습니다.
JEP 354 : JDK-13의 스위치 표현식 (미리보기) 및 JEP 361 : JDK-14의 스위치 표현식 (표준) 은 스위치 문 을 확장하여 표현식 으로 사용할 수 있습니다.
이제 다음을 수행 할 수 있습니다.
case L ->
)을 사용하십시오.
"case L->"스위치 레이블의 오른쪽에있는 코드는 표현식, 블록 또는 (편의를 위해) throw 문으로 제한됩니다.
스위치 식에서 값을 산출하기 위해
break
with value 문이 문을 대신하여 삭제됩니다yield
.
따라서 답변 ( 1 , 2 ) 의 데모 는 다음과 같습니다.
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");
}
Groovy의 산들 바람입니다. groovy jar을 포함시키고 groovy
이러한 모든 작업을 수행하기 위해 유틸리티 클래스를 작성하고 Java에서해야 할 일을 더 많이 찾습니다 (기업에서 Java 6을 사용하고 있기 때문에).
it.'p'.each{
switch (it.@name.text()){
case "choclate":
myholder.myval=(it.text());
break;
}}...
intellij를 사용할 때 다음 사항도 확인하십시오.
파일-> 프로젝트 구조-> 프로젝트
파일-> 프로젝트 구조-> 모듈
모듈이 여러 개인 경우 모듈 탭에서 올바른 언어 수준을 설정해야합니다.
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;
}
}
}