자바에서 배열(Array)과 리스트(ArrayList)를 다룰 때 자주 마주치는 두 가지 현상이 있습니다. 배열을 출력할 때 발생하는 해시코드 출력 문제와, ArrayList를 배열로 변환할 때 new String[0]을 인자로 넘기는 이유입니다.
오늘은 이 두 가지 문법의 정확한 동작 원리를 자바의 내부 메커니즘을 통해 설명하겠습니다.
배열을 생성하고 System.out.println()으로 직접 출력하면 배열의 요소가 아닌 식별할 수 없는 문자열이 출력됩니다.
String[] arr = {"apple", "banana", "cherry"};
System.out.println(arr);
// 출력 결과: [Ljava.lang.String;@15db9742
동작 원리:
자바에서 배열은 원시 타입(Primitive type)이 아닌 객체(Object)로 취급됩니다. 따라서 System.out.println() 메서드에 배열 객체를 넘기면 내부적으로 최상위 클래스인 Object의 toString() 메서드가 호출됩니다.
Object.toString()은 기본적으로 [객체의 타입 시그니처 + @ + 메모리 주소(해시코드)] 형태의 문자열을 반환하도록 구현되어 있습니다. 따라서 위와 같은 결과가 나오는 것입니다.
해결 방법:
배열 내부에 존재하는 실제 데이터 요소들을 확인하려면 java.util.Arrays 클래스의 Arrays.toString() 메서드를 사용해야 합니다.
System.out.println(Arrays.toString(arr));
// 출력 결과: [apple, banana, cherry]
Arrays.toString()은 내부적으로 for문을 돌며 배열의 각 요소(element)에 접근하여 하나의 조합된 문자열(String)로 반환해주기 때문에, 우리가 원하는 데이터를 직관적으로 확인할 수 있습니다.
ArrayList를 일반 배열로 변환할 때, 단순히 toArray() 메서드만 호출하지 않고 특정 타입의 빈 배열(new String[0])을 매개변수로 전달하는 방식을 주로 사용합니다.
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
// ArrayList를 배열로 변환
String[] arr = list.toArray(new String[0]);
이 코드가 작성되는 이유는 크게 두 가지 기술적 배경을 갖습니다.
자바의 제네릭은 컴파일 타임에만 타입 안정성을 검사하고, 런타임(실행 시간)에는 타입 정보가 지워지는 '타입 소거' 특징을 가집니다.
즉, ArrayList<String>은 컴파일이 끝나면 내부적으로 데이터를 Object 타입으로 관리합니다. 따라서 매개변수 없이 list.toArray()를 호출하면 반환되는 배열의 타입은 String[]이 아닌 Object[]가 되어버립니다.
이를 해결하기 위해 new String[0]이라는 타입 지정용 인스턴스를 넘겨주어, 반환받을 배열의 타입이 String임을 명시하는 것입니다.
인자로 넘긴 배열의 크기가 리스트의 실제 데이터 크기보다 작을 경우, 자바의 toArray() 메서드는 내부적으로 다음과 같이 동작합니다.
size와 동일한 크기의 새로운 배열을 내부적으로 메모리에 할당한다.과거에는 메모리 할당 비용을 줄이기 위해 list.toArray(new String[list.size()])처럼 리스트의 크기를 직접 계산해서 넘기는 방식이 권장되었습니다.
하지만 최신 JVM(Java 6 이후)에서는 길이가 0인 배열을 전달하는 것이 오히려 성능상 더 유리하도록 최적화되었습니다. 사전에 크기를 명시하는 방식은 리플렉션(Reflection) 비용과 동기화 오버헤드가 발생할 수 있기 때문에, 빈 배열(new String[0])을 넘겨 JVM이 내부적으로 최적화된 방식으로 새 배열을 생성하도록 위임하는 것이 현재의 표준 컨벤션입니다.
Arrays.toString(배열): 1차원 배열의 내용물을 깔끔하게 보여준다.Arrays.deepToString(배열): 2차원 배열뿐만 아니라, 3차원, 4차원 등 모든 다차원 배열의 내부 값을 보여준다.2차원 배열이상에서 행의 길이를 알고 싶으면 arr[i].length;로 사용한다.