CS
If와 Switch
dlxortmd123
2022. 11. 2. 01:59
의문점
If와 Swith가 내부적으로 어떤 차이가 있고, 어떤 상황에 사용하면 좋을까?
내부적으로 어떻게 동작할까?
If-else
- 원래 코드
public static void IfTest(int num) {
if (num == 1) {
System.out.println("숫자 1입니다.");
} else if (num == 2) {
System.out.println("숫자 2입니다.");
} else {
System.out.println("알 수 없는 숫자입니다.");
}
}
- 바이트 코드
public static IfTest(I)V
L0
LINENUMBER 21 L0
ILOAD 0
ICONST_1
IF_ICMPNE L1
L2
LINENUMBER 22 L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\uc22b\uc790 1\uc785\ub2c8\ub2e4."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
GOTO L3
L1
LINENUMBER 23 L1
FRAME SAME
ILOAD 0
ICONST_2
IF_ICMPNE L4
L5
LINENUMBER 24 L5
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\uc22b\uc790 2\uc785\ub2c8\ub2e4."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
GOTO L3
L4
LINENUMBER 26 L4
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\uc54c \uc218 \uc5c6\ub294 \uc22b\uc790\uc785\ub2c8\ub2e4."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L3
LINENUMBER 28 L3
FRAME SAME
RETURN
L6
LOCALVARIABLE num I L0 L6 0
MAXSTACK = 2
MAXLOCALS = 1
- if 문은 조건을 검사하고 다음으로 넘기고 또 다음 조건을 검사하는 식의 반복으로 이루어진다. 따라서 조건이 많아지게 되고, 만약 해당하는 조건이 뒤에 있다면 거쳐야 하는 명령어가 많아지게 된다.
- IF_ICMPNE 명령어를 통해 인자를 비교한다.
switch
- 원래 코드
public static void switchTest(int num) {
switch (num) {
case 1:
System.out.println("숫자 1입니다.");
break;
case 2:
System.out.println("숫자 2입니다.");
break;
default:
System.out.println("알 수 없는 숫자입니다.");
}
}
- 바이트 코드
public static switchTest(I)V
L0
LINENUMBER 8 L0
ILOAD 0
LOOKUPSWITCH
1: L1
2: L2
default: L3
L1
LINENUMBER 10 L1
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\uc22b\uc790 1\uc785\ub2c8\ub2e4."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L4
LINENUMBER 11 L4
GOTO L5
L2
LINENUMBER 13 L2
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\uc22b\uc790 2\uc785\ub2c8\ub2e4."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
LINENUMBER 14 L6
GOTO L5
L3
LINENUMBER 16 L3
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\uc54c \uc218 \uc5c6\ub294 \uc22b\uc790\uc785\ub2c8\ub2e4."
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
LINENUMBER 18 L5
FRAME SAME
RETURN
L7
LOCALVARIABLE num I L0 L7 0
MAXSTACK = 2
MAXLOCALS = 1
- switch는 컴파일 시 tableswitch 또는 lookupswitch 를 이용한다.
- tableswitch는 switch 문의 케이스들이 만들어질 테이블의 target offset에 인덱스로 효율적으로 표현될 때 사용한다. 만약 switch로 들어온 표현식이 인덱스의 범위를 벗어난 경우, default target이 사용된다.
- 위의 예로 들면 1부터 시작한 offset 테이블을 생성할 수 있고, 3과 같이 범위를 벗어난 경우 default target 으로 넘어가게 된다.
- 만약 케이스들이 산개해(흩어져) 있다면, lookupswitch 를 이용한다. 이 경우는 케이스의 값을 인덱스로 활용하여 산개한 케이스에 대해 더 효율적으로 대처할 수 있다.
- JVM은 선형 검색(O(n)) 보다 효율적으로 검색하기 위해, lookupswitch를 case의 키로 정렬한다.
- lookupswitch는 들어온 expression과 키 값을 대조해야 하는 과정이 존재해서, 단순히 범위를 체크하고 테이블에 인덱스하는 tableswitch에 비해 비효율적일 수 있습니다.
- 공간이 충분하면 tableswitch, 공간의 여유가 없다면 lookupswitch를 사용하는 것이 좋아보인다.
- 예시
Method int chooseFar(int)
0 iload_1
1 lookupswitch 3:
-100: 36
0: 38
100: 40
default: 42
36 iconst_m1
37 ireturn
38 iconst_0
39 ireturn
40 iconst_1
41 ireturn
42 iconst_m1
43 ireturn
Method int chooseFar(int)
0 iload_1
1 lookupswitch 3:
-100: 36
0: 38
100: 40
default: 42
36 iconst_m1
37 ireturn
38 iconst_0
39 ireturn
40 iconst_1
41 ireturn
42 iconst_m1
43 ireturn
- switch 문의 또 다른 특징으로는 인자로 받는 expression은 int 데이터만 받는 점이다. 예를 들어 byte, char, short 은 int로 승격되어 컴파일된다. 따라서 그 외에 double, float 등은 int 형으로 변환 후 expression에 넣어주어야 한다.
- 그렇다면, JAVA 7부터 지원하는 switch 문에 String을 넣는 것은 어떻게 동작할까?
- 바로 hashcode를 사용한다. hashcode를 통해 int 값으로 변환 후 lookupswitch에 넣어 사용한다.
- tableswitch 대신 lookupswitch를 사용하는 이유는 아무래도 hashcode 값은 연속될 가능성이 거의 없기 때문인가보다…
어떤 상황에 사용하면 좋을까?
- 일단 기본적으로 조건이 적으면 별 차이가 없을 것 같다. 하지만 조건이 많아지면 case 문을 쓰는 것이 좋아보인다.
- 만약 if 문을 쓰더라도, 자주 쓰는 조건을 앞에 두면 실행하는 명령어가 적어 더 효율적일 것이다.