[Java] Java switch의 문자열 비교, 내부 원리와 성능 분석

2025. 2. 13. 20:09Java/Practical

이 글에서는 Java의 switch문에서 문자열(String) 비교 시, equals()를 명시적으로 사용하지 않고도 올바르게 비교하기 위한 switch의 동작 과정에 대해 알아보겠습니다.

 

  • switch에서 문자열을 비교하는 방법
  • switch의 내부 동작 원리
  • switch vs if-else
  • switch 사용 시 주의사항
  • 정리
  • 회고
  • 참고 및 예제 코드

switch에서 문자열을 비교하는 방법

Java 7 이후에는 정수나 열거형 뿐 아니라 문자열도 switch문의 조건으로 사용할 수 있습니다. 이는 switch의 값과 case의 문자열을 String.equals 를 사용한 것처럼 비교하기 때문입니다.

The switch statement compares the String object in its expression with the expressions associated with each case label as if it were using the String.equals method

Strings in switch Statements

package pl.java.stringswitch;

public class StringSwitchExample {
    public static void main(String[] args) {
        String A = "Hello";

        switch (A) {
            case "Hello":
                System.out.println("Hi!");
                break;
            case "Hello World":
                System.out.println("Hello World");
                break;
            default:
                System.out.println("who are you?");
        }
    }
}

switch의 내부 동작 원리

그렇다면 switch에서는 어떻게 문자열을 올바르게 비교할 수 있을까요?

Java 컴파일러는 if-else 구조의 String.equals 보다. switch(string) 을 더욱 효율적인 바이트 코드로 변환한다고 합니다.

The Java compiler generates generally more efficient bytecode from switch statements that use String objects than from chained if-then-else statements.

이를 확인해보기 위해 StringSwitchExample.java 을 컴파일하고, 바이트코드를 확인해보겠습니다.

1. StringSwitchExample.java 컴파일

javac 명령어로 StringSwitchExample.java를 컴파일해서 클래스 파일을 생성합니다.

javac StringSwitchExample.java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package pl.java.D250205;

public class StringSwitchExample {
    public StringSwitchExample() {
    }

    public static void main(String[] var0) {
        switch ("Hello") {
            case "Hello" -> System.out.println("Hi!");
            case "Hello World" -> System.out.println("Hello World");
            default -> System.out.println("who are you?");
        }

    }
}

 

2. StringSwitchExample.class 바이트 코드 확인하기

javap 명령어로 컴파일 된 StringSwitchExample.class 의 바이트 코드를 출력합니다.

javap -c StringSwitchExample.class
Compiled from "StringSwitchExample.java"
public class pl.java.D250205.StringSwitchExample {
  public pl.java.D250205.StringSwitchExample();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #7                  // String Hello
       2: astore_1
       3: aload_1
       4: astore_2
       5: iconst_m1
       6: istore_3
       7: aload_2
       8: invokevirtual #9                  // Method java/lang/String.hashCode:()I
      11: lookupswitch  { // 2
            -862545276: 50
              69609650: 36
               default: 61
          }
      36: aload_2
      37: ldc           #7                  // String Hello
      39: invokevirtual #15                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      42: ifeq          61
      45: iconst_0
      46: istore_3
      47: goto          61
      50: aload_2
      51: ldc           #19                 // String Hello World
      53: invokevirtual #15                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      56: ifeq          61
      59: iconst_1
      60: istore_3
      61: iload_3
      62: lookupswitch  { // 2
                     0: 88
                     1: 99
               default: 110
          }
      88: getstatic     #21                 // Field java/lang/System.out:Ljava/io/PrintStream;
      91: ldc           #27                 // String Hi!
      93: invokevirtual #29                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      96: goto          118
      99: getstatic     #21                 // Field java/lang/System.out:Ljava/io/PrintStream;
     102: ldc           #19                 // String Hello World
     104: invokevirtual #29                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     107: goto          118
     110: getstatic     #21                 // Field java/lang/System.out:Ljava/io/PrintStream;
     113: ldc           #35                 // String who are you?
     115: invokevirtual #29                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     118: return
}

이를 해석하면 다음과 같습니다.

  1. switch의 값과 모든 case의 문자열을 hashCode로 변환.
  2. lookupswitch를 이용해 hashCode값과 일치하는지 확인
  3. 일치하는 경우 String.equals 를 실행해 해쉬 충돌 방지 및 최종 검증
  4. caseIndex를 설정해 case 블록 선택

switch vs if-else

switch는 문자열의 hashCode 을 빠르게 비교 후 일치하는 경우에만 String.equals 로 최종 검증을 하지만, if-else구조는 모든 조건 분기에서 String.equals 를 사용합니다.

따라서 다음과 같이 상황에 따라 적절하게 사용하면 됩니다.

  • 성능이 중요하고 case가 많으면 switch
  • 조건이 복잡하거나 동적인 비교가 필요하다면 if-else

switch 사용 시 주의사항

switch를 사용하기에 앞서, 올바르게 사용하지 않으면 다음과 같은 문제가 발생할 수 있습니다.

 

[ 대소문자의 구분 ]

switch로 문자열 비교시 최종적으로 String.equals를 사용하므로 대소문자를 구분해야 합니다.

consequently, the comparison of String objects in switch statements is case sensitive.

 

 

[ NullPointerException ]

switch의 값이 null일 경우 null.hashCode() 는 NPE를 발생 시킵니다.

같은 이유에서 case의 값도 null일 수 없습니다.

따라서 switch의 값에 대한 null 확인 및 case의 값에 null 비교에 주의해야 합니다.

 


정리

  • switch 문에서도 정확한 문자열 비교가 가능하다.
  • switch 문에서는 hashCode로 문자열을 빠르게 비교 후 String.equals로 최종 검증한다.
  • switch의 값이 null일 경우 NPE가 발생하니 주의하자.

회고

switch에는 기본 타입(원시 타입)만 비교할 수 있다고 막연히 생각하고 있었는데, 내부적으로 equals 메서드를 활용해 문자열까지 비교할 수 있다는 점이 신기했다. 더 놀라웠던 것은 이 기능이 최근 도입된 게 아니라 Java 7부터 이미 제공되고 있었다는 사실이다. 평소 자연스럽게 사용하던 문법에도 이런 발전 히스토리가 숨어 있다는 것을 새삼 느꼈다.

이번에는 바이트코드를 직접 열어보며 컴파일러가 switch 문을 어떻게 최적화하는지 확인해봤는데, 처음엔 코드가 낯설고 이해하기 어려웠다. 하지만 AI를 활용하면서 동작 원리를 단계별로 따라갈 수 있었고, 공식 문서에서 설명하는 해시 기반 분기 → equals 최종 검증 흐름이 실제 바이트코드에서도 동일하게 구현된다는 것을 직접 눈으로 확인할 수 있었다. “문서를 읽고 끝나는 것”이 아니라, 실제 결과물을 뜯어보며 근거를 찾아가는 과정이 꽤 흥미로웠다.

이번 경험을 통해 언어를 사용할 때 단순히 문법만 외우는 것이 아니라, 언어가 어떤 기능을 지원하고 컴파일러가 어떤 방식으로 이를 처리하는지 이해하는 습관이 중요하다는 점을 다시 깨달았다. 이런 이해는 성능적인 선택이나 문제 해결 방식에서 더 나은 결정을 내리는 데 도움이 될 것이다.

앞으로도 새로운 문법이나 기능을 접하면 공식 문서만 보지 말고, 가능하다면 바이트코드나 내부 동작까지 직접 확인해보고 싶다. 작은 호기심에서 시작한 탐구가 학습의 깊이를 크게 넓혀주는 경험이었다.


참고 및 예제 코드

https://docs.oracle.com/javase/7/docs/technotes/guides/language/strings-switch.html

 

Strings in switch Statements

Strings in switch Statements In the JDK 7 release, you can use a String object in the expression of a switch statement: public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) { String typeOfDay; switch (dayOfWeekArg) { case "Monday": typeOfDay

docs.oracle.com

 

https://github.com/rogi-rogi/tech-blog-code/blob/main/pl/java/D250205/StringSwitchExample.java

 

tech-blog-code/pl/java/D250205/StringSwitchExample.java at main · rogi-rogi/tech-blog-code

이 저장소는 기술 블로그에서 사용한 다양한 코드 예제와 스니펫들을 모아둔 곳입니다. 각 코드 샘플은 블로그 포스트와 함께 관리되며, 참고 및 재사용을 위해 구성되어 있습니다. - rogi-rogi/tech

github.com