본문 바로가기
Programming Language

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

by kimyoungrok 2025. 2. 5.
728x90

목차

  • switch에서 문자열을 비교하는 방법
  • switch의 내부 동작 원리
  • switch vs if-else
  • switch 사용 시 주의사항

Java 7부터는 switch문에서 문자열(String)을 비교할 수 있습니다. 기존의 if-else에서 equals()를 사용하는 방식보다 더욱 깔끔하게 문자열을 비교할 수 있습니다.

이번 글에서는 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

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

 

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.stringswitch;

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.stringswitch.StringSwitchExample {
  public pl.java.stringswitch.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 비교에 주의해야 합니다.

 

https://github.com/rogi-rogi/tech-blog-code/tree/main/pl/java/stringswitch

 

728x90