자바의 데이터 타입은 2가지가 존재합니다.
기본 타입은 1:1 대응으로 참조 타입
을 하나씩 가지고 있습니다.
이를 박싱된 기본 타입
이라고 부릅니다. (Integer, Double, Boolean)
기본 타입은 값만 가지고 있지만, 박싱된 기본 타입은 값에 더해 식별성이라는 속성도 가집니다. (각 객체는 자신만의 고유한 주소값을 가지고 있는 존재)
두 개의 박싱된 기본 타입은 값(value)가 같아도 결국 서로 다르다라는 것을 의미합니다.
class Case1 {
public static void main(String[] args) {
Comparator<Integer> naturalOrder = (i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);
int compare = naturalOrder.compare(new Integer(42), new Integer(42));
System.out.println("compare = " + compare);
}
}
위의 코드를 실행해보면
compare = 1
이라는 결과가 나옵니다. 이를 통해 바로 첫 번째 문제를 알 수 있습니다.
분명 의도한대로라면 0
(동일함)이 나와야 하는데 결과가 1
(다름)이 나옵니다.
그 이유는 비교 연산자 ==
는 객체끼리를 비교하였을때 주소를 비교하기 때문에 false
이기 때문에 1
이 나옵니다. 이를 객체 참조의 식별성 검사라고 합니다.
equals()
였다면 올바른 결과가 나타났을 것입니다.
가장 이상적인 것은Integer.compare()
을 사용하는 것입니다.
기본 타입의 값은 언제나 유효한 값이 들어갑니다. 하지만 박싱된 기본 타입은 참조 타입이기 때문에 유효하지 않은 값인 null
이 들어갈 수 있습니다.
public class Case2 {
static Integer i;
public static void main(String[] args) {
if (i == 42) {
System.out.println("믿을 수 없군!");
}
}
}
위 코드는 아무것도 출력되지 않는 것을 의도한 코드겠지만, 그 전에 에러가 발생한다.
i == 42를 할 때, null 참조를 언박싱하게 되면서 NullPointException
에러가 발생한다.
i의 타입을 Integer에서 int로 바꿔주면 에러가 해결된다.
class Case3 {
public static void main(String[] args) {
boxingTest();
unboxingTest();
}
public static void boxingTest() {
Long sum = 0L;
long start = System.currentTimeMillis();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("박싱 타입 소요시간: " + (end - start));
}
public static void unboxingTest() {
long sum = 0L;
long start = System.currentTimeMillis();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("언방식 타입 소요시간: " + (end - start));
}
}
위 코드를 실행해보면
위 코드는 박싱과 언박싱이 반복해서 일어나기 때문에 체감될 정도로 성능이 느립니다.
기본 타입이 간단하고 빠르고 신경을 덜 써도 되니, 웬만하면 기본 타입을 사용하자
참고로 아래의 경우엔 박싱된 기본 타입을 사용합니다.