[Java] 문자열 비교 (==, equals)와 intern

델버·2022년 5월 19일
0

Java

목록 보기
7/8
String a = "Android";
String b = "Android";
String c = new String("Android");

System.out.println(a == b); // true
System.out.println(a == c); // false
Systen.out.println(a.equals(c)) // true
  • 문자열을 == 연산자로 비교할 때, 분명 문자열은 같은데 false가 나오는 경우가 있다. 그런데 equal() 메소드를 사용하면 true가 나온다. 대체 == 연산자와 equal 메소드는 무슨 차이가 있을까?

String 생성

  • String 생성하는 것에 literal을 이용하는 것(String interning)과 생성자(new String())를 이용하는 것이 있다.

  • a처럼 만드는 방식을 ‘literal’(interning)을 이용했다고 한다. a에서 literal을 이용해 “Android”의 값을 가진 객체를 생성하면 그 값은 String pool이라는 곳에 들어간다. 그리고 b가 literal로 “Android”를 생성하면 마찬가지로 String pool로 가게 되는데, 이미 “Android” 값이 있기 때문에 넣지 않고 이미 존재하는 “Android”값을 참조하게 된다. 자세히 말하면 String pool에 있는 객체를 a와 b가 공유하게 된다. 이렇게 되면 메모리와 성능 효율이 더 좋다.

  • 하지만 new String()을 이용해 객체를 생성한 c는, 존재하는 값이 있더라도 String pool로 저장되지 않고 Heap의 별도로 값이 저장된다. 그래서 a와 c는 서로 참조하는 주소값이 다르기 때문에 false가 나오는 것이다.

  • new String()으로 같은 값을 계속 만들 경우 메모리를 비효율적으로 차지하게된다.

  • 이때 더 추가로 알 수 있는 것은 String은 immutable하다고 한다. 즉 String 클래스의 객체는 불변하는 것인데, 이 immutable 때문에 String pool에 있는 객체를 공유하여 사용할 수 있는 것이다.

  • 만약 a가 참조하는 값인 String pool의 “Android”가 “Studio”로 변경이 가능하다면 b가 참조하고 있는 값도 바뀌게 되는 아찔아찔한 상황이 된다.
    만약 수정한다면(a의 참조 값을 바꾼다고 하면) a = “Studio” 로 “Studio”를 String pool에 저장해 참조하게 되는 것이다. 이때 b = “Studio”가 추가되면 “”Android”는 사용하지 않게 되어 GC의 대상이 된다.

== 연산자 & equals() 메소드

  • == 는 참조하는 주소를 비교한다.

  • a과 b는 String pool에 있는 “Android”라는 문자열을 참조한다. 하지만 c는 new String()으로 객체가 생성되어 a와 b가 참조하고 있는 주소값과는 다른 주소값을 참조하게 된다. 이는 String을 생성하는 방식과 그것을 메모리에 담는 방식 그리고 참조하는 값이 달라서 생긴 문제다.

  • equals()는 참조하는 값을 비교한다.

  • == 연산자가 주소를 비교했다면 equal는 값을 비교한다는 것이다. 그래서 a와 c가 참조하는 문자열 값은 같기 때문에 a.equals(c)가 true가 나오는 것이다.
    equals()는 처음에 == 연산자와 같이 참조하는 주소값을 비교하고 만약 다르게 될 경우 그 때 값을 비교하게 된다.

  • 참고로 a.equals(b)에서 a가 null이면 NullPointerException이 발생한다. 하지만 그 반대인 b가 null인 경우는 발생하지 않는다.

intern()

  • String 클래스에 있는 intern() 메서드가 있다. 이는 앞서 리터럴 방식으로 만드는 것과 같은 행동을 한다. c 처럼 생성자로 만든 “Android”는 String pool에 들어가지 않는다고 했는데, 이를 String pool에 넣어주고 이미 있으면 그곳에 문자열을 반환하는 것이 intern() 메소드이다.
String x = "Android";
String y = new String("Android");
String z = y.intern();

System.out.println(x == y); // false
System.out.println(x == z); // true
  • x와 y는 앞서 본 것처럼 다르지만, z에서 intern()을 사용하여 “Android”를 String pool에 넣게 된다. 먼저 문자열이 이미 있는지 보고 없으면 넣게 되지만 이미 x로 인하여 pool에 있기 때문에 넣지 않고 바로 문자열을 반환한다. 그래서 x와 z는 같은 곳을 참조하게 된다.

  • 논외로 Java에는 Call by Value만 있다는 흥미로운 글이 있어 가져왔다.
    <[Java] Java는 Call by reference가 없다>
    https://deveric.tistory.com/92

참고

0개의 댓글