5.4 Arrays 유틸리티 클래스 (Arrays Utility Class)
자바는 배열을 다룰 때 개발자가 직접 반복문을 작성하지 않고도 출력, 복사, 정렬, 검색 등을 손쉽게 처리할 수 있도록 java.util.Arrays 클래스를 제공합니다. 이 클래스의 모든 메서드는 static이므로 객체 생성 없이 바로 사용합니다.
1. 배열 출력: Arrays.toString() / Arrays.deepToString()
배열을 println()으로 직접 출력하면 의미 없는 메모리 주소값이 나옵니다.
import java.util.Arrays;
int[] arr = {10, 20, 30, 40, 50};
System.out.println(arr); // [I@7ef88735 (주소값, 의미 없음)
System.out.println(Arrays.toString(arr)); // [10, 20, 30, 40, 50]
2차원 배열: deepToString()
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println(Arrays.toString(matrix)); // [[I@..., [I@..., ...] (잘못된 방법)
System.out.println(Arrays.deepToString(matrix)); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
2. 배열 정렬: Arrays.sort()
배열 요소를 오름차순 으로 정렬합니다. 내부적으로 Dual-Pivot Quicksort 알고리즘을 사용합니다.
기본 정렬 (오름차순)
int[] numbers = {5, 3, 8, 1, 9, 2, 7, 4, 6};
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers));
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
String[] names = {"Charlie", "Alice", "Bob", "David"};
Arrays.sort(names);
System.out.println(Arrays.toString(names));
// [Alice, Bob, Charlie, David]
부분 정렬
int[] arr = {5, 3, 8, 1, 9, 2, 7};
Arrays.sort(arr, 1, 5); // 인덱스 1~4(5 미만)만 정렬
System.out.println(Arrays.toString(arr));
// [5, 1, 3, 8, 9, 2, 7] - 1번~4번 인덱스만 정렬됨
내림차순 정렬 (Comparator 사용)
기본형 배열은 Comparator를 직접 사용할 수 없으므로 Integer[]로 변환하거나, Java 8+에서는 IntStream을 활용합니다.
// Integer[] 사용 (박싱)
Integer[] nums = {5, 3, 8, 1, 9};
Arrays.sort(nums, Comparator.reverseOrder()); // 내림차순
System.out.println(Arrays.toString(nums)); // [9, 8, 5, 3, 1]
// String 배열 내림차순
String[] words = {"banana", "apple", "cherry"};
Arrays.sort(words, Comparator.reverseOrder());
System.out.println(Arrays.toString(words)); // [cherry, banana, apple]
// 길이순 정렬
Arrays.sort(words, Comparator.comparingInt(String::length));
System.out.println(Arrays.toString(words)); // [apple, banana, cherry]
3. 이진 탐색: Arrays.binarySearch()
정렬된 배열에서 특정 값을 O(log n) 시간에 검색합니다.
반드시 정렬 후 사용
binarySearch()는 배열이 오름차순 정렬된 상태 에서만 정확한 결과를 반환합니다.
int[] sorted = {10, 20, 30, 40, 50, 60, 70};
Arrays.sort(sorted); // 이미 정렬되어 있지만 습관적으로 확인
int idx = Arrays.binarySearch(sorted, 40);
System.out.println("40의 위치: " + idx); // 3
int notFound = Arrays.binarySearch(sorted, 35);
System.out.println("35의 위치: " + notFound); // 음수 (없음)
반환 값:
- 값이 존재하면: 해당 인덱스 (0 이상)
- 값이 없으면:
-(삽입 위치) - 1(음수)
// String 배열에도 사용 가능
String[] names = {"Alice", "Bob", "Charlie", "David", "Eve"};
Arrays.sort(names);
int pos = Arrays.binarySearch(names, "Charlie");
System.out.println("Charlie의 위치: " + pos); // 2
int notFound = Arrays.binarySearch(names, "Frank");
System.out.println("Frank의 위치: " + notFound); // 음수 (-6)
4. 배열 채우기: Arrays.fill()
배열의 모든 요소(또는 일부)를 특정 값으로 채웁니다.
int[] arr = new int[5];
// 전체 채우기
Arrays.fill(arr, 7);
System.out.println(Arrays.toString(arr)); // [7, 7, 7, 7, 7]
// 부분 채우기 (인덱스 1~3)
Arrays.fill(arr, 1, 4, 99);
System.out.println(Arrays.toString(arr)); // [7, 99, 99, 99, 7]
// 초기화 용도
int[] board = new int[10];
Arrays.fill(board, -1); // -1로 초기화 (미방문 표시 등)
System.out.println(Arrays.toString(board)); // [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
5. 배열 복사: Arrays.copyOf() / Arrays.copyOfRange()
int[] original = {1, 2, 3, 4, 5};
// 동일 크기 복사
int[] copy1 = Arrays.copyOf(original, original.length);
System.out.println(Arrays.toString(copy1)); // [1, 2, 3, 4, 5]
// 더 큰 배열로 복사 (나머지는 기본값 0)
int[] copy2 = Arrays.copyOf(original, 8);
System.out.println(Arrays.toString(copy2)); // [1, 2, 3, 4, 5, 0, 0, 0]
// 더 작은 배열로 복사 (잘림)
int[] copy3 = Arrays.copyOf(original, 3);
System.out.println(Arrays.toString(copy3)); // [1, 2, 3]
// 범위 지정 복사 (인덱스 1 이상 4 미만)
int[] copy4 = Arrays.copyOfRange(original, 1, 4);
System.out.println(Arrays.toString(copy4)); // [2, 3, 4]
// 범위가 배열 길이를 초과하면 0으로 채움
int[] copy5 = Arrays.copyOfRange(original, 3, 8);
System.out.println(Arrays.toString(copy5)); // [4, 5, 0, 0, 0]
6. 배열 비교: Arrays.equals() / Arrays.deepEquals()
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
int[] c = {1, 2, 4};
System.out.println(a == b); // false (다른 객체)
System.out.println(Arrays.equals(a, b)); // true (내용이 같음)
System.out.println(Arrays.equals(a, c)); // false (내용이 다름)
// 2차원 배열 비교
int[][] m1 = {{1, 2}, {3, 4}};
int[][] m2 = {{1, 2}, {3, 4}};
System.out.println(Arrays.equals(m1, m2)); // false (얕은 비교)
System.out.println(Arrays.deepEquals(m1, m2)); // true (깊은 비교)
7. 배열 → List 변환: Arrays.asList()
배열을 List로 변환하여 컬렉션 API를 사용할 수 있습니다.
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
String[] arr = {"사과", "바나나", "오렌지"};
// 고정 크기 List (add/remove 불가!)
List<String> fixedList = Arrays.asList(arr);
System.out.println(fixedList); // [사과, 바나나, 오렌지]
// fixedList.add("포도"); // UnsupportedOperationException!
// 수정 가능한 List로 만들려면 새 ArrayList에 복사
List<String> mutableList = new ArrayList<>(Arrays.asList(arr));
mutableList.add("포도");
System.out.println(mutableList); // [사과, 바나나, 오렌지, 포도]
Arrays.asList() 주의사항
Arrays.asList()가 반환하는 List는 고정 크기 입니다. set()(값 변경)은 가능하지만 add(), remove()는 불가합니다. 또한 기본형 배열(int[])은 List<int[]>가 되므로 Integer[]를 사용해야 합니다.
int[] primitives = {1, 2, 3};
List<int[]> wrong = Arrays.asList(primitives); // List의 원소가 int[] 배열 1개!
Integer[] boxed = {1, 2, 3};
List<Integer> correct = Arrays.asList(boxed); // [1, 2, 3]
8. 배열 → Stream 변환: Arrays.stream()
Java 8+에서 배열을 스트림으로 변환하여 함수형 처리가 가능합니다.
import java.util.Arrays;
int[] numbers = {3, 1, 4, 1, 5, 9, 2, 6};
// 합계
int sum = Arrays.stream(numbers).sum();
System.out.println("합계: " + sum); // 31
// 최대/최소
int max = Arrays.stream(numbers).max().getAsInt();
int min = Arrays.stream(numbers).min().getAsInt();
System.out.println("최대: " + max + ", 최소: " + min); // 최대: 9, 최소: 1
// 평균
double avg = Arrays.stream(numbers).average().getAsDouble();
System.out.println("평균: " + avg); // 3.875
// 필터링 후 배열로 변환
int[] evens = Arrays.stream(numbers).filter(n -> n % 2 == 0).toArray();
System.out.println("짝수만: " + Arrays.toString(evens)); // [4, 2, 6]
// 각 요소 변환 (2배)
int[] doubled = Arrays.stream(numbers).map(n -> n * 2).toArray();
System.out.println("2배: " + Arrays.toString(doubled)); // [6, 2, 8, 2, 10, 18, 4, 12]
9. Collections 유틸리티 간단 소개
java.util.Collections는 List에 사용하는 유틸리티 클래스입니다. 배열을 List로 변환 후 활용합니다.
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 9, 2));
// 정렬
Collections.sort(list);
System.out.println(list); // [1, 2, 3, 5, 8, 9]
// 최대/최소
System.out.println(Collections.max(list)); // 9
System.out.println(Collections.min(list)); // 1
// 무작위 셔플
Collections.shuffle(list);
System.out.println(list); // 임의 순서로 섞임
// 역순
Collections.reverse(list);
System.out.println(list); // 현재 순서의 역순
// 빈도 수
List<String> words = Arrays.asList("apple", "banana", "apple", "cherry", "apple");
System.out.println(Collections.frequency(words, "apple")); // 3
10. 실전 예제: 학생 이름 배열 정렬 및 이진 탐색
import java.util.Arrays;
import java.util.Scanner;
public class StudentSearch {
public static void main(String[] args) {
String[] students = {
"홍길동", "김민준", "이서연", "박지훈", "최수아",
"정도윤", "강하은", "윤시우", "임지아", "오준서"
};
System.out.println("원본 명단:");
System.out.println(Arrays.toString(students));
// 오름차순 정렬
Arrays.sort(students);
System.out.println("\n정렬된 명단:");
System.out.println(Arrays.toString(students));
// 이진 탐색으로 학생 검색
Scanner scanner = new Scanner(System.in);
System.out.print("\n검색할 학생 이름: ");
String target = scanner.next();
int index = Arrays.binarySearch(students, target);
if (index >= 0) {
System.out.println(target + "님은 " + (index + 1) + "번째에 있습니다.");
} else {
System.out.println(target + "님은 명단에 없습니다.");
// 삽입 위치 계산: -(index + 1)
int insertPos = -(index + 1);
System.out.println("추가하려면 " + (insertPos + 1) + "번째에 삽입하세요.");
}
// 추가 통계
System.out.println("\n이름 길이 통계:");
int totalLen = 0;
for (String s : students) totalLen += s.length();
System.out.printf("평균 이름 길이: %.1f자%n", (double) totalLen / students.length);
int longestIdx = 0;
for (int i = 1; i < students.length; i++) {
if (students[i].length() > students[longestIdx].length()) {
longestIdx = i;
}
}
System.out.println("가장 긴 이름: " + students[longestIdx]);
scanner.close();
}
}
실행 예시:
원본 명단:
[홍길동, 김민준, 이서연, 박지훈, 최수아, 정도윤, 강하은, 윤시우, 임지아, 오준서]
정렬된 명단:
[강하은, 김민준, 박지훈, 오준서, 윤시우, 이서연, 임지아, 정도윤, 최수아, 홍길동]
검색할 학생 이름: 이서연
이서연님은 6번째에 있습니다.
11. Arrays 메서드 빠른 참조표
| 메서드 | 설명 | 예시 |
|---|---|---|
toString(arr) | 1차원 배열을 문자열로 | [1, 2, 3] |
deepToString(arr) | 다차원 배열을 문자열로 | [[1, 2], [3, 4]] |
sort(arr) | 오름차순 정렬 | - |
sort(arr, from, to) | 부분 정렬 | - |
sort(arr, comparator) | 커스텀 정렬 | - |
binarySearch(arr, key) | 이진 탐색 | 인덱스 또는 음수 |
fill(arr, val) | 전체 채우기 | - |
fill(arr, from, to, val) | 부분 채우기 | - |
copyOf(arr, len) | 길이 지정 복사 | - |
copyOfRange(arr, from, to) | 범위 복사 | - |
equals(a, b) | 1차원 배열 비교 | true/false |
deepEquals(a, b) | 다차원 배열 비교 | true/false |
asList(arr) | 배열 → 고정 List | - |
stream(arr) | 배열 → Stream | - |
고수 팁
실무에서 Arrays 활용 패턴:
- 디버깅:
System.out.println(Arrays.toString(arr))으로 배열 내용을 빠르게 확인 - 배열 확장:
Arrays.copyOf(arr, arr.length * 2)로 배열을 2배 크기로 늘리기 - 방어적 복사: 메서드에서 배열을 반환할 때
return arr.clone()으로 원본 보호 - equals 대체: 단위 테스트에서
Arrays.equals(expected, actual)로 배열 검증
// 방어적 복사 예시
public class SafeArray {
private int[] data;
public SafeArray(int[] input) {
this.data = input.clone(); // 외부 배열 변경 방어
}
public int[] getData() {
return data.clone(); // 내부 배열 노출 방어
}
}