java, String의 비교를 어떻게 해야하는가? `==` vs equals() 과 string constant pool

string constant pool

  • String은 string constant pool을 사용한다.
  • String은 객체지만 같은 문자열이 이미 존재하면 새로운 메모리에 등록하지 않고 기존에 있던 메모리를 가리키도록 한다.
  • 그러므로 같은 문자열에 대해서는 기본적으로 동일하며 동등하다.
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2); // true
  • 하지만 new String()을 사용할 경우 더 이상 풀에 저장하지 않고 별도의 힙에 저장한다.
  • 그러므로 아래와 같은 상황에서는 동일성 비교가 동작하지 않는다. 다만 동등성 비교(equals)는 동작한다.
String s1 = "abc";
String s2 = new String("abc");
String s3 = new String("abc");

System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // false
System.out.println(s1 == s3); // false

System.out.println(s1.equals(s2)); // true
System.out.println(s2.equals(s3)); // true
System.out.println(s1.equals(s3)); // true
  • 성능이나 사용성 때문에 String을 초기화 할 때 객체를 새로 생성하는 것보다 리터럴로 대입하는 것을 권장한다.

그럼 동일성 비교를 하면 되는 건가?

  • 리터럴로 대입하는 것을 권장한다는 의미는, equals메서드를 사용할 필요가 없다는 의미인가? 그렇지 않다.
  • 왜냐하면 new String()을 사용하는 경우가 있기 때문이다.
  • 특히 문자열 합성에 자주 사용하는 StringBuilder#toString은 new String을 사용한다.
    • toString 메서드의 끝은 다음과 같다.
    • StringUTF16#newString : return new String(Arrays.copyOfRange(val, index << 1, last << 1), UTF16);
  • 실제로 테스트를 할 경우 동일성 비교를 false로 응답한다.
String s1 = new StringBuilder("abc").toString();
String s2 = new StringBuilder("abc").toString();

System.out.println("abc" == "abc"); // true
System.out.println(s1==s2); // false
System.out.println("abc" == s1); // false
  • 결과적으로 String은 equals를 사용하거나 별도의 라이브러리를 통해 비교하는 것이 안전해 보인다.
  • 다만 equals는 NPE에 취약하며 이를 고려해야 한다.