개발하다보면 분기 처리를 하는게 필수적이어진다.
이때 switch를 사용하는 게 나은지 else if 의 사용이 나은지 고민이 될 때가 있다.
보통 코드 정리는 switch가 한 눈에 보기 쉬운 편이고,
개인적으로 사용하기에는 if~else 문이 처리하기가 쉬운 것 같다.
그래도 코드 가시성 및 개발자 편의성을 고려하지 않을 수는 없지만...
제일 중요한 건 프로그램 성능이라... 두 가지의 로직적인 차이를 정리해보고자 한다.
switch문
변수를 미리 입력 받아 두고, 미리 정해놓은 값들과 일치 여부를 판단하여 control flow를 처리
if~else문
boolean으로 결과 값이 나오는 조건문의 결과에 따라 true, false 두가지 control flow를 가진다.
그러므로 . . .
if~else 문을 모두 switch로 변환 시키지는 못하지만
switch 문으로 작성된 문법을 모두 if~else 로 변환 시키는 것은 가능하다.
결과적으로 두개를 같은 기능을 하도록 구현은 할 수 있지만
구조적으로는 어떨까?
else if 구문과 switch 구문의 logic적인 차이
if else 구문
각각 조건문을 반복하며 control flow를 결정한다.
N개의 if else 문이 있으면 N개 조건문의 진위여부를 판단.
Switch 구문
JVM에서 switch 구문안의 case 값들의 분포에 따라
내부적으로 각각의 case에 최적화된 2개의 자바 바이트코드를 생성하게 되는데...
공통적으로 두 경우 모두 hashtable이 연상되는 구조를 띈다.
그리고 case 값에 따라 최적화 루틴을 설정하게 된다.
- case값들이 서로 큰 차이없이 붙어있는 경우: TableSwitch 형식의 컴파일 수행
- case값들이 서로 차이가 크게 날 경우 : LookupSwitch 형식의 컴파일 수행
TableSwitch 방식
case로 주어진 값과 case들 사이의 값들에 해당하는 case까지 전부 계산하여 바이트코드로 생성하는 방식
HashTable 과 유사한 구조 : 자바 바이트 코드에서 lable만을 사용
LookupSwitch 방식
TableSwitch 방식의 코드와는 다르게, 사이 값들을 해시 형태로 계산해두지 않음.
List 와 유사한 구조 : 자바 바이트 코드에서 key와 함께 label을 사용
최종적으로...
Switch 구문은 item의 개수(N)에 따라 O(lgN) (최악의 경우) 에 실행되고
if~else 구문은 item의 개수(N)에 따라 실행된다.. O(N)
검증
동일 결과 값을 대상으로 for 문 중첩 하여 테스트를 시켜보자.
// in case: if~else 구문 사용
long a = System.currentTimeMillis();
int ret = -1;
for (int i=0; i<1000000000; ++i)
for (int num=0; num<5; ++num)
if (num == 0) ret = 10;
else if (num == 1) ret = 20;
else if (num == 2) ret = 30;
else if (num == 3) ret = 40;
else if (num == 4) ret = 50;
else ret = 12;
long b = System.currentTimeMillis();
System.out.println(ret);
System.out.println(b-a+"ms");
50
8718ms
// in case: Switch 구문 사용
long a = System.currentTimeMillis();
int ret = -1;
for (int i=0; i<1000000000; ++i)
for (int num=0; num<5; ++num)
switch (num) {
case 0: ret = 10; break;
case 1: ret = 20; break;
case 2: ret = 30; break;
case 3: ret = 40; break;
case 4: ret = 50; break;
default: ret = 12; break;
}
long b = System.currentTimeMillis();
System.out.println(ret);
System.out.println(b-a+"ms");
50
2820ms
분기 구문이 약 5개 정도에 불과한데에도 switch문이 3~4배 정도 빠른 것을 확인 가능하다.
분기 구문이 늘어날수록 차이는 벌어질 것으로 예상된다.
요약
앞에서 말했듯이 보통 구현 시간이나 익숙하다는 이유로
'하나의 변수 값을 받아 그 값을 비교해 control flow를 정하는 코드'를 구현할 때
if~else 구문을 많이 채택하게 된다.
하지만 속도 같은 퍼포먼스 측면에서는 switch 문이 우세하므로...
참고하여 버릇을 다시 들이는 것도 좋을테다.
+)
case문의 경우 연산을 한 정보를 가지고 있고, 그걸로 결과 값을 가져다 주므로
"메모리" 측면에서 단점을 가지긴 한다.
분기 3개까지는 if~else문. 그 이상의 분기는 switch~case문.