회사 인턴을 진행하면서 equals와 equalsIgnoreCase, == 을 혼용하여 사용하였는 데
사수님께서 각각 차이점이 무엇인지 알고
사용하는 것이 좋을 거 같다고 하셔서 쓰게 된 포스팅입니다.
자바의 데이터 타입
먼저 자바의 데이터 타입입니다.
자바에서는 크게 데이터 타입이 원시타입(primitive type) 과 참조타입(reference type)으로 나뉘어집니다.
원시타입 (primitive type)
정수, 실수, 문자 논리 리터럴 등의 실제 데이터 값을 저장하는 타입으로 아래 항목들이 해당됩니다.
- int, long, double, float, boolean, byte, short, char
참조타입 (reference type)
객체(Object),를 참조(주소 저장) 하는 타입으로 메모리 번지 값을 통해 객체를 참조하는 타입으로 아래 항목들과 인터페이스, 클래스, 배열, 열거형이 포함됩니다.
- Integer, Long, Double, Float, Boolean, Byte, Short, Char
- 기본 자료타입을 객체로 다루기 위해서 사용하는 래퍼클래스
여기에서 래퍼클래스란? (int와 Integer의 차이점에 대해서 찾아보시면 좋을 거 같습니다.)
• 래퍼클래스는 기본형을 표현해야 할 때 사용
• 기본형에 null 값을 저장해야할때(VO 정수형에 null값이 필요한 경우 등)
• 제네릭타입과 호환할때 (List<Integer>)
• 컬렉션 프레임워크를 사용할 때(HashMap< String, Integer>)
• 메서드에 전달된 파라미터의 값을 수정하거나, 객체와 함께 동작하는 기능을 수행하면서 참조를 공유하여 데이터 간 일관성을 유지할 때
== 연산자
- == 연산자는 원시타입에 대해서 값을 비교하고, 참조타입에서는 주소를 비교합니다.
- 원시 타입도 사실은 constant pool 에서 값을 비교하기 때문에 주소를 비교하는 것과 같다고 볼 수도 있습니다.
- 같은 상수를 참조하면 주소값이 같으니 결국 같은 값이면 동일하다고 판단합니다.
- 원시타입에 대해서는 equals() 연산이 존재하지 않습니다.
여기서 constant pool이란 ?
런타임에 생성되는 Static 상수 저장소
Constant Pool 객체에 저장상수풀은 정적영역에 있는 메모리이기에 GC의 대상이 아니다.
자바 8로 넘어가면서 정적 static영역은 StackOverFlow 발생 위험이 있기 때문에(상수의 개수가 지속적으로 늘어나게 되면) Heap 구조로 바뀌게 되었다.
위에 말을 예시를 통해서 보여드리겠습니다.
- 같은 값이여도 선언 방식에 따라서 주소(참조)값이 달라지기 때문에 참조타입에서는 아래와 같은 결과가 나오게 됩니다.
String s1 = "Hello"; //리터널로 선언 (여기서 리터널이란 데이터 자체를 의미한다.)
String s2 = "Hello";
String s3 = new String("Hello"); //객체로 생성
String s4 = new String("Hello");
System.out.print(s1==s2); //true
System.out.print(s1==s3); //false
System.out.print(s3==s4); //false
- 문자열 리터널로 선언한 경우 String Constant pool 영역에 같은 값의 문자열을 공유하여 메모리 사용량을 최적화합니다.
- 하지만 새로운 문자열 객체로 선언한 경우 Heap영역에 저장되어 다른 주소값을 할당받게 되면서 주소 참조가 이뤄지기 때문에 값을 확인하기 위해서는 equals()를 사용해야합니다.
* 여기에서 String Constant pool는 앞선 constant pool 과 다른 개념입니다. *
equals()
- equals는 최상위 클래스인 Object에 포함되어있기 때문에 하위 모든 클래스에서 재정의해서 사용이 가능합니다.
- 실제 Object의 equals를 확인하면 == 연산자를 사용하여 주소를 비교합니다.
public boolean equals(Object obj) {
return (this == obj);
}
- 하지만 String 클래스에서 equlas는 주소를 비교하는 것이 아닌 값을 비교해야합니다.
- String 클래스에서는 equals가 같은 주소값을 가졌다면 true를 반환하고, 값은 주소값을 가지지 않았다면 equals를 재정의하여 내용을 비교하게 됩니다.
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
equalsIgnoreCase()
- equalsIgnoreCase() 도 String 클래스에서 기본으로 제공하는 메소드로, 이름과 같이 대소문자를 구분하지 않고, 두 문자열을 비교합니다.
- equals와 마찬가지로 주소값을 먼저 확인하고 내용을 비교합니다.
- toLowercase, toUpperCase를 사용하지 않아도 되기 때문에 대소문자 구분없이 값을 비교해야할 때 더 간편하게 사용됩니다.
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.length() == length())
&& regionMatches(true, 0, anotherString, 0, length());
}
결론
- 원시타입인 정수, 실수, 문자 논리 리터럴을 비교할 땐 == 연산자를 통해서 주소값을
- String, 래퍼클래스와 같은 참조타입은 equals 를 사용하여 값을 비교합니다.
- 대소문자 구분 없이 값을 비교할 땐 equalsIgnoreCase를 사용합니다.
'개발 > JAVA' 카테고리의 다른 글
[자바의 정석] 형식화 클래스 2-2 SimpleDateFormat (2) | 2024.01.11 |
---|---|
[자바의 정석] 형식화 클래스 2-1 DeciamlFormat (4) | 2024.01.10 |
[자바의 정석] Calendar와 Date (3) | 2024.01.08 |
[JAVA] StringTokenizer (0) | 2023.07.31 |