본문 바로가기

Programing/JVM(Java, Kotlin)

[Java] switch와 String 그리고 바이트코드

제목이 거창하다.

사실 카페에서 본 질문에 댓글을 달다가 공부한 것을 정리하는 것이다.


질문은 아래와 같다.

- 질문 제목 : switch에서 charAt사용 차이  

- 질문 내용 :아래 두 코딩에 차이점이 뭔가요??
 처음에 charAt를 사용하지 않고 해보다가 저렇게해도 결과가 나오긴 했는데 charAt를 사용하라고 하셔서 사용한 것으로 바꾸긴 했는데 차이점이 뭔지 궁금합니다!
 둘다 결과가 제대로 나오긴합니다.

 


차이

왼쪽의 경우 char 타입으로 switch 안에서 분기를 하고 있고, 오른쪽은 String 타입으로 하고 있다. 변수명이 대문자로 시작하는 것이 거슬릴 수 있으나 초심자의 코드라고 생각하고 넘어가자.

그러면 이런 차이는 어떤 결과를 가져오게 되는가?

바이트 코드 관점 (TABLESWITCH / LOOKUPSWITCH)

Java 코드가 컴파일되면 class 파일이라는 바이트 코드로 바뀐다.
switch는 자바컴파일러(javac)에 의해 tableswitch와 lookupswitch 명령으로 컴파일된다.
tableswitch 명령은 case의 값들이 대상들의 오프셋들의 표(테이블)로 바꾸기 적당할 때 사용이 된다.
반면 lookupswitch 명령은 case의 대상들이 너무 흩어져 있어서 테이블로 구성하였을 때 빈 공간(이를 sparse라 부른다.)이 많을 경우에 룩업(찾아보는)하는 방식으로 동작이 된다.
자세한 내용은 JVM 스펙 Compiling Switches에 예로 나와 있으니 확인을 해보는 것이 좋다.
stackoverflow에 올라온 답변도 있으니 도움이 된다.

타입 관점

위의 질문자의 예에서는 char를 사용을 하였는데 byte, char, short 값들은 내부적으로 int를 쓴 것과 동일하게 사용된다.
하지만 String의 경우는 좀 특별한데 Java7에서 추가된 타입이기 때문이었다.
자바7 이전 버전의 경우 switch ( ) 안에 String을 넣으면 잘못된 문법이라고 에러를 낸다.

String의 경우는 String의 해시코드(String.hashCode())이용하여 1차적인 lookupswitch를 수행하는 것을 확인할 수 있었다.
다른 문자열이만 해시코드가 같을 수도 있기 때문에 equals 메서드를 이용한 값 동일성을 통해 해당 case에 해당하는 로직으로 분기를 하는 것을 알 수 있었다.

결론

따라서 성능적인 부분에서 String을 분기 조건으로 사용하는 경우에 byte, char, short, int을 쓰는 것보다 효율성이 떨어진다는 것을 알 수 있다.
다만, 이런 성능의 저하가 편의성을 대체 할만큼 크지는 않다고 생각한다. 적재 적소에 맞게 쓰는 본인의 능력을 갖추는 것이 더 중요할 것 같다.