코드 한 줄의 기록

Java 배열(1차원/다차원)과 Arrays 유틸리티 완벽 정리: 초보자부터 실무까지 한 번에! 본문

JAVA

Java 배열(1차원/다차원)과 Arrays 유틸리티 완벽 정리: 초보자부터 실무까지 한 번에!

CodeByJin 2025. 9. 29. 00:17
반응형

배열은 자바 프로그래밍의 기초 중의 기초라고 할 수 있습니다. 처음 배울 때는 단순해 보이지만, 실제로 활용하다 보면 정말 많은 기능과 메서드들이 있다는 걸 깨닫게 되죠.

 

이번 글에서는 Java의 1차원/다차원 배열부터 Arrays 클래스의 유용한 메서드들까지 차근차근 살펴보겠습니다. 같이 공부하는 마음으로 준비했으니 천천히 따라와 주세요!

1차원 배열 기초부터 탄탄하게

배열 선언의 세 가지 방법

자바에서 배열을 선언하는 방법은 생각보다 다양합니다.

// 방법 1: 타입 옆에 대괄호
int[] numbers;

// 방법 2: 배열명 앞에 대괄호 (공백 있음)
int []scores;

// 방법 3: 배열명 뒤에 대괄호 (C언어 스타일)
int grades[];

 

개인적으로는 첫 번째 방법을 선호합니다. 타입을 보면 바로 '아, 이건 배열이구나'라는 걸 알 수 있거든요.

 

배열 초기화 방법들

배열을 초기화하는 방법도 여러 가지가 있습니다.

// 1. 크기만 지정 (기본값으로 초기화)
int[] arr1 = new int[5]; // [0, 0, 0, 0, 0]

// 2. 선언과 동시에 값 설정
int[] arr2 = {1, 2, 3, 4, 5};

// 3. new 키워드와 함께 값 설정
int[] arr3 = new int[]{10, 20, 30};

// 4. 반복문으로 초기화
int[] arr4 = new int[5];
for(int i = 0; i < arr4.length; i++) {
    arr4[i] = i * 2; // [0, 2, 4, 6, 8]
}

 

배열 출력하기

배열을 출력할 때 흔히 하는 실수가 있습니다.

int[] numbers = {1, 2, 3, 4, 5};

// 잘못된 방법 - 이상한 값이 출력됨
System.out.println(numbers); // [I@15db9742 같은 해시코드

// 올바른 방법 - Arrays.toString() 사용
System.out.println(Arrays.toString(numbers)); // [1, 2, 3, 4, 5]

 

처음에 저도 이 부분에서 당황했던 기억이 나네요. 배열 자체를 출력하면 메모리 주소가 나오니까 반드시 Arrays.toString()을 사용해야 합니다.

다차원 배열 정복하기

2차원 배열의 이해

2차원 배열은 '배열의 배열'이라고 생각하면 됩니다. 게임에서 맵을 표현하거나 표 형태의 데이터를 다룰 때 정말 유용하죠.

// 2차원 배열 선언 및 초기화
int[][] matrix = new int[3][4]; // 3행 4열

// 값 설정과 동시에 초기화
int[][] scores = {
    {90, 85, 92, 88},
    {76, 94, 82, 90},
    {88, 79, 95, 87}
};

// 반복문으로 초기화
int[][] table = new int[3][4];
for(int i = 0; i < table.length; i++) {
    for(int j = 0; j < table[i].length; j++) {
        table[i][j] = (i + 1) * (j + 1);
    }
}

 

여기서 중요한 건 table.length는 행의 개수를, table[i].length는 각 행의 열 개수를 나타낸다는 점입니다.

 

비정방형 배열의 활용

자바의 특별한 기능 중 하나가 바로 비정방형 배열입니다. 각 행마다 열의 개수가 다를 수 있어서 메모리를 효율적으로 사용할 수 있죠.

// 비정방형 배열 생성
int[][] irregular = new int[4][];
irregular[0] = new int[1]; // 첫 번째 행: 1개 열
irregular[1] = new int[3]; // 두 번째 행: 3개 열
irregular[2] = new int[2]; // 세 번째 행: 2개 열
irregular[3] = new int[4]; // 네 번째 행: 4개 열

// 생성과 동시에 초기화
int[][] triangle = {
    {1},
    {1, 2},
    {1, 2, 3},
    {1, 2, 3, 4}
};

 

3차원 배열과 그 이상

3차원 배열도 가능하지만, 실제로는 특별한 경우가 아니면 잘 사용하지 않습니다.

// 3차원 배열 - 2x3x4 크기
int[][][] cube = new int[2][3][4];

// 값 설정
for(int i = 0; i < cube.length; i++) {
    for(int j = 0; j < cube[i].length; j++) {
        for(int k = 0; k < cube[i][j].length; k++) {
            cube[i][j][k] = i + j + k;
        }
    }
}

Arrays 클래스: 배열의 만능 도구

java.util.Arrays 클래스는 배열을 다루는 데 필요한 거의 모든 기능을 제공합니다. 모든 메서드가 static이라서 객체 생성 없이 바로 사용할 수 있어요.

 

배열 정렬과 검색

가장 많이 사용하는 기능 중 하나가 정렬입니다.

int[] numbers = {5, 2, 8, 1, 9, 3};

// 오름차순 정렬
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers)); // [1, 2, 3, 5, 8, 9]

// 이진 검색 (정렬된 배열에서만 사용!)
int index = Arrays.binarySearch(numbers, 5);
System.out.println("5의 인덱스: " + index); // 3

 

주의할 점: binarySearch()는 반드시 정렬된 배열에서만 사용해야 합니다. 정렬되지 않은 배열에서 사용하면 잘못된 결과가 나올 수 있어요.

 

배열 복사의 다양한 방법

int[] original = {1, 2, 3, 4, 5};

// 전체 복사
int[] copy1 = Arrays.copyOf(original, original.length);

// 일부만 복사
int[] copy2 = Arrays.copyOf(original, 3); // [1, 2, 3]

// 크기를 늘려서 복사 (빈 공간은 0으로 채워짐)
int[] copy3 = Arrays.copyOf(original, 7); // [1, 2, 3, 4, 5, 0, 0]

// 범위 지정 복사
int[] copy4 = Arrays.copyOfRange(original, 1, 4); // [2, 3, 4]

 

배열 채우기와 비교

// 배열을 특정 값으로 채우기
int[] arr = new int[5];
Arrays.fill(arr, 10); // [10, 10, 10, 10, 10]

// 람다식으로 채우기
Arrays.setAll(arr, i -> i * 2); // [0, 2, 4, 6, 8]

// 배열 비교
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
boolean isEqual = Arrays.equals(arr1, arr2); // true

 

다차원 배열을 위한 특별 메서드

int[][] matrix1 = {{1, 2}, {3, 4}};
int[][] matrix2 = {{1, 2}, {3, 4}};

// 다차원 배열 비교
boolean isEqual = Arrays.deepEquals(matrix1, matrix2); // true

// 다차원 배열 출력
System.out.println(Arrays.deepToString(matrix1)); // [[1, 2], [3, 4]]

 

일반 equals()toString()은 다차원 배열에서 제대로 작동하지 않으니 반드시 deep 버전을 사용해야 합니다.

실무에서 자주 쓰는 배열 패턴들

배열과 List 변환

실무에서는 배열과 List를 자주 변환해야 합니다.

// 배열을 List로 변환
Integer[] intArray = {1, 2, 3, 4, 5};
List<Integer> list = Arrays.asList(intArray);

// 주의: 이렇게 만든 List는 크기가 고정됨!
// list.add(6); // UnsupportedOperationException 발생

// 가변 List가 필요하면 새로 생성
List<Integer> mutableList = new ArrayList<>(Arrays.asList(intArray));

 

배열 크기 조정 패턴

public class DynamicArray {
    private int[] array;
    private int size;
    
    public DynamicArray(int initialCapacity) {
        array = new int[initialCapacity];
        size = 0;
    }
    
    public void add(int value) {
        if (size >= array.length) {
            // 배열 크기 두 배로 확장
            array = Arrays.copyOf(array, array.length * 2);
        }
        array[size++] = value;
    }
    
    public int[] toArray() {
        return Arrays.copyOf(array, size);
    }
}

 

2차원 배열 순회 최적화

int[][] matrix = new int[1000][1000];

// 일반적인 순회
for(int i = 0; i < matrix.length; i++) {
    for(int j = 0; j < matrix[i].length; j++) {
        matrix[i][j] = i * j;
    }
}

// 향상된 for문 활용
for(int[] row : matrix) {
    Arrays.fill(row, 5); // 모든 요소를 5로 설정
}

성능과 메모리 고려사항

배열 vs ArrayList 선택 기준

배열과 ArrayList 중 어떤 걸 선택할지는 늘 고민되는 부분입니다.

배열을 선택하는 경우 ArrayList를 선택하는 경우
크기가 고정되어 있을 때 크기가 동적으로 변경될 때
메모리 사용량을 최소화하고 싶을 때 편리한 메서드들이 필요할 때
원시 타입(int, double 등)을 저장할 때 객체 타입을 저장할 때
성능이 중요한 경우 객체 타입을 저장할 때

 

메모리 효율적인 배열 사용법

// 메모리 효율적인 초기화
int[] largeArray = new int[1000000];
Arrays.fill(largeArray, 0); // 명시적으로 0으로 초기화

// 불필요한 복사 피하기
public void processArray(int[] arr) {
    // 원본 배열 직접 수정 (복사하지 않음)
    for(int i = 0; i < arr.length; i++) {
        arr[i] *= 2;
    }
}

실제 코딩에서 유용한 팁들

배열 디버깅 꿀팁

public static void printArrayInfo(int[] arr, String name) {
    System.out.println(name + " 정보:");
    System.out.println("- 길이: " + arr.length);
    System.out.println("- 내용: " + Arrays.toString(arr));
    System.out.println("- 해시코드: " + arr.hashCode());
    System.out.println();
}

// 사용 예시
int[] testArray = {1, 2, 3, 4, 5};
printArrayInfo(testArray, "테스트 배열");

 

예외 상황 처리

public static int safeGetElement(int[] arr, int index) {
    if (arr == null) {
        throw new IllegalArgumentException("배열이 null입니다.");
    }
    if (index < 0 || index >= arr.length) {
        throw new IndexOutOfBoundsException("인덱스가 범위를 벗어났습니다: " + index);
    }
    return arr[index];
}

 

배열 검증 유틸리티

public class ArrayUtils {
    public static boolean isEmpty(int[] arr) {
        return arr == null || arr.length == 0;
    }
    
    public static boolean contains(int[] arr, int value) {
        return Arrays.binarySearch(Arrays.copyOf(arr, arr.length), value) >= 0;
    }
    
    public static int[] removeElement(int[] arr, int index) {
        if (isEmpty(arr) || index < 0 || index >= arr.length) {
            return arr;
        }
        
        int[] result = new int[arr.length - 1];
        System.arraycopy(arr, 0, result, 0, index);
        System.arraycopy(arr, index + 1, result, index, arr.length - index - 1);
        return result;
    }
}

 

Java의 배열과 Arrays 클래스는 정말 강력한 도구입니다. 처음에는 단순해 보이지만, 깊이 파면 팔수록 다양한 활용법이 나오죠. 특히 실무에서는 성능과 메모리 효율성을 고려한 배열 사용이 중요합니다.

 

저도 이 글을 정리하면서 다시 한번 배열의 다양한 기능들을 확인할 수 있었어요. 특히 비정방형 배열이나 deepEquals() 같은 메서드들은 알아두면 정말 유용할 것 같습니다.

 

앞으로 배열을 사용할 때는 단순히 데이터를 저장하는 용도로만 생각하지 말고, Arrays 클래스의 다양한 메서드들을 적극 활용해보세요. 코드가 훨씬 깔끔해지고 효율적이 될 거예요!

반응형