개발 일지/Java

[Java] Integer.valueOf(127)==Integer.valueOf(127)은 True?

배발자 2023. 8. 8.
반응형

문제점

얼마전 백준에서 문제를 풀다가 도저히 이해가 안되는 상황이 발생하였다. Integer타입을 가진 어레이 리스트를 활용해서 "==" 비교 연산을 하였다. 참조형 타입 같은 경우 "==" 연산을 할 경우 메모리 주소 값을 비교하는 것으로 알고 있다. 하지만 다음과 같은 코드를 봤을 때 조금 의문이 발생하였다. 

 

        ArrayList<Integer> listA = new ArrayList<>();
        ArrayList<Integer> listB = new ArrayList<>();

        listA.add(128);
        listB.add(128);

        if (listA.get(0) == listB.get(0)) {
            System.out.println("True");
        }
        else {
            System.out.println("False");
        }

 

위의 코드문을 봤을 때 출력 값이 어떻게 됐을까? 

listA와 listB에 각각 new ArrayList<>를 생성하여 각각 128이라는 동일한 값을 넣어주었다. Integer 타입 상 참조 유형이기 때문에 서로 다른 메모리 주소를 가리키고 있기에 출력문은 다음과 같다. 이거에 대한 의구심은 없었다. 

 

출력값 

 False

 

근데 조금 의아한게 뭐냐면, 이번엔 128이 아니라 127을 넣어보겠다. 

 

        ArrayList<Integer> listA = new ArrayList<>();
        ArrayList<Integer> listB = new ArrayList<>();

        listA.add(127);
        listB.add(127);

        if (listA.get(0) == listB.get(0)) {
            System.out.println("True");
        }
        else {
            System.out.println("false");
        }

 

출력값 

True

 

왜? 난 이 현상이 도저히 내 상식에서 이해가 가지 않았다. 필자가 알고 잇는 기본 유형과 참조 유형은 다음과 같다. 

 

Java의 기본유형은 총 8가지로 (byte, short, int, long, float, double, char 및 boolean) 이진 비트 형식이다. 즉, a = 127, b = 127로 대입한다면 이진수 값 127을 직접 보유하므로 실제로 비교했을 때 a==b 는 True를 반환한다. 

 

참조 유형은 기본 유형 외에 클래스, 인터페이스, 열거형, 배열 등이 속하며 개체 자체가 아닌 개체의 주소를 보유하게 된다. 예를 들어, Integer a = new Integer (127) , Integer b = new Integer(127)은 이진 값을 보유하지 않고 두 개의 개별 개체의 메모리 주소를 보유한다. 즉, 두 개의 값은 개체 자체가 아닌 개체의 메모리 주소를 비교하므로 절대절대 일치하는 값이 나오면 안된다는 것이 나의 생각이였다.

 

하지만 Integer 객체에 127을 담고 있을 땐 같은 메모리 주소를 가리키고 있고 128일 땐 다른 메모리를 가리키고 있다는 게 이해가 안간다는 것이다.

 

127뿐만이 아니였다. -128부터 127까지는 모두 True로 뜨고 그 밖의 범위에서는 모두 False가 뜬다는 것이다. 

 

이유

개발 툴에서 Integer 클래스로 들어가서 "127"을 검색해봤는데 IntegerCache라는 클래스가 존재하였다. 오잉 이게 뭐지?? 라는 생각으로 해당 코드를 읽어보았다. 다음과 같이 정의되어있었다. 

 

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

 

위의 코드를 분석해보면 -128부터 high 값까지의 정수를 캐싱하는 기능을 수행한다.

high값은 'VM.getSavedProperty("java.lang.Integer.IntegerCache.high")' 를 통해 시스템 프로퍼티로부터 가져오거나 그게 아니라면 기본적으로 127로 설정된다. high값이 설정되면 -128부터 high까지의 값을 캐시 배열에 'Integer" 객체로 만들어서 저장한다. 

 

즉, Java는 기본적으로 -128에서 127 범위에 속하는 정수 객체를 캐시한다는 것이다. 이 정수 범위는 일부 메모리를 간접적으로 절약하는 일상적인 프로그래밍에서 많이 사용된다는 이유라고 한다. 

 

필자가 테스트 한 결과 값이 왜 True 값이 출력되었는지 드디어 해소가 되었다. 

Integer내부에서 캐시 역할을 하는 정적 클래스를 유지하고 -128에서 127까지의 정수 개체를 보유하므로 정수 개체를 얻으려고 할 때 항상 동일한 개체를 반환하기 때문인것이다. 다시말해, -128부터 127까지는 고정 캐싱 범위를 가지므로 같은 메모리 주소를 반환하니, True가 뜰 수 밖에!

 

Integer 뿐만 아니라 Byte, Short, Long에서 각각 ByteCache, ShortCache, LongCache가 정의되어있었고 -128에서 128 사이의 고정 캐싱 범위를 가지고 있는 것을 확인하였다. 

 

마치며

순간 자바를 처음부터 공부를 해야하는건가 생각이 들 정도로 아찔했다. 원시타입, 참조타입을 이해하고 있던 나의 과거 지식들이 한순간에 무너지는 순간을 느끼긴했지만 다행히 내가 알고 있던 개념은 확실하다. 하지만 내부적으로 캐싱 시스템이 동작하고 있었다는 점을 새롭게 알게 되어서 JAVA의 고귀함(?)을 한 번 더 느낀다. 

반응형

'개발 일지 > Java' 카테고리의 다른 글

[Java] HashSet의 비밀  (4) 2023.10.04
[Java] 해시 충돌  (0) 2022.12.19
[Java] equals 와 hashCode  (0) 2022.12.18
[Java] public static void main 인 이유?  (0) 2022.12.18
[Java] Java 8 / Java 11 버전 별 특징  (0) 2022.12.17

댓글