Java에서 데이터를 저장하는 방법 중 대표적인 두 가지는 배열(Array)과 리스트(List)이다.
배열은 고정된 크기의 연속된 메모리 공간, 리스트는 크기가 동적으로 변할 수 있는 컬렉션이다.
1️⃣ 배열(Array)
배열은 같은 타입의 여러 개의 데이터를 저장하는 자료구조다.
배열 선언 및 초기화
📌 1차원 배열 선언 및 초기화
// 배열 선언
int[] arr1; // 일반적인 선언 방식
int arr2[]; // C 스타일의 선언 방식 (비추천)
// 배열 선언 + 크기 지정 (초기값: 0)
int[] arr3 = new int[5]; // [0, 0, 0, 0, 0]
// 배열 선언 + 초기화
int[] arr4 = {1, 2, 3, 4, 5}; // 크기는 자동 설정
int[] arr5 = new int[]{10, 20, 30}; // new int[] 필요 (메서드 내부에서 사용 시)
📌 2차원 배열 선언 및 초기화
// 2차원 배열 선언
int[][] matrix1 = new int[3][4]; // 3행 4열 배열 (초기값: 0)
// 2차원 배열 선언 + 초기화
int[][] matrix2 = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
배열의 데이터 추가, 접근, 삭제
📌 요소 접근 및 변경
int[] arr = {10, 20, 30, 40, 50};
System.out.println(arr[2]); // ✅ 30 (0부터 시작)
// 값 변경
arr[1] = 100; // arr = [10, 100, 30, 40, 50]
📌 요소 추가 및 삭제 (배열은 크기 고정 → 추가 및 삭제 개념 없음)
배열의 요소를 삭제하는 방법은 없고, 특정 인덱스를 null이나 기본값으로 설정하는 방식으로 처리한다.
arr[2] = 0; // 해당 위치 값을 0으로 설정 (완전한 삭제는 아님)
배열의 크기 확인
System.out.println(arr.length); // ✅ 5
배열 요소 출력
📌 1차원 배열 출력
- Arrays.toString() 활용
import java.util.Arrays;
int[] arr = {10, 20, 30, 40};
System.out.println(Arrays.toString(arr));
// 출력: [10, 20, 30, 40]
- for문
int[] arr = {10, 20, 30, 40};
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
// 출력: 10 20 30 40
for (int num : arr) {
System.out.print(num + " ");
}
// 출력: 10 20 30 40
📌 2차원 배열 출력
- Arrays.deepToString() 활용
import java.util.Arrays;
System.out.println(Arrays.deepToString(matrix2));
- for문
for (int[] row : matrix2) {
System.out.println(Arrays.toString(row));
}
배열 정렬
📌 오름차순 정렬
1. Arrays.sort() 사용
import java.util.Arrays;
int[] arr = {5, 1, 3, 2, 4};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
// 출력: [1, 2, 3, 4, 5]
2. Stream API 사용 (Java 8+)
import java.util.Arrays;
import java.util.stream.IntStream;
int[] arr = {5, 3, 8, 1, 2};
arr = Arrays.stream(arr).sorted().toArray();
System.out.println(Arrays.toString(arr));
// 출력: [1, 2, 3, 5, 8]
3. 람다식 사용 (Java 8+)
import java.util.Arrays;
Integer[] arr = {5, 3, 8, 1, 2};
Arrays.sort(arr, (a, b) -> a - b);
System.out.println(Arrays.toString(arr));
// 출력: [1, 2, 3, 5, 8]
- Arrays.sort()를 사용할 때, 람다식 정렬은 Comparator<T>를 활용하는 방식이다. Comparator<T>는 참조 타입에 대해서만 동작하기 때문에 기본 자료형인 int[] 배열은 람다식을 사용할 수 없다.
📌 내림차순 정렬
1. Arrays.sort() 사용
import java.util.Arrays;
import java.util.Comparator;
Integer[] arr = {5, 3, 8, 1, 2};
Arrays.sort(arr, Comparator.reverseOrder());
System.out.println(Arrays.toString(arr));
// 출력: [8, 5, 3, 2, 1]
- Integer[]로 변환해야 Comparator.reverseOrder()를 사용할 수 있음.
2. Stream API 사용 (Java 8+)
import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.IntStream;
int[] arr = {5, 3, 8, 1, 2};
arr = Arrays.stream(arr)
.boxed()
.sorted(Comparator.reverseOrder())
.mapToInt(Integer::intValue)
.toArray();
System.out.println(Arrays.toString(arr));
// 출력: [8, 5, 3, 2, 1]
- int[]는 Comparator.reverseOrder()를 사용할 수 없으므로 boxed()를 사용해 변환해야 함.
3. 람다식 사용 (Java 8+)
import java.util.Arrays;
Integer[] arr = {5, 3, 8, 1, 2};
Arrays.sort(arr, (a, b) -> b - a);
System.out.println(Arrays.toString(arr));
// 출력: [8, 5, 3, 2, 1]
- Arrays.sort()를 사용할 때, 람다식 정렬은 Comparator<T>를 활용하는 방식이다. Comparator<T>는 참조 타입에 대해서만 동작하기 때문에 기본 자료형인 int[] 배열은 람다식을 사용할 수 없다.
배열의 유용한 메서드
1. Arrays.copyOf()
배열을 복사할 때 사용한다. 새로운 배열을 만들고, 기존 배열의 일부 또는 전부를 복사할 수 있다.
import java.util.Arrays;
int[] arr = {1, 2, 3, 4, 5};
int[] copiedArr = Arrays.copyOf(arr, 3); // 배열의 처음 3개 요소 복사
System.out.println(Arrays.toString(copiedArr));
// 출력: [1, 2, 3]
2. Arrays.fill()
배열을 특정 값으로 채우는 메서드이다.
int[] arr = new int[5];
Arrays.fill(arr, 10); // 모든 요소를 10으로 채움
System.out.println(Arrays.toString(arr));
// 출력: [10, 10, 10, 10, 10]
3. Arrays.equals()
두 배열이 같은지 비교할 때 사용한다.
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
int[] arr3 = {3, 2, 1};
System.out.println(Arrays.equals(arr1, arr2)); // ✅ true
System.out.println(Arrays.equals(arr1, arr3)); // ✅ false
4. Arrays.sort()
배열을 오름차순으로 정렬할 때 사용한다.
int[] arr = {5, 3, 8, 1, 2};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
// 출력: [1, 2, 3, 5, 8]
5. Arrays.binarySearch()
배열에서 이진 탐색을 통해 특정 값의 인덱스를 찾을 때 사용한다. 배열은 정렬되어 있어야 한다.
int[] arr = {1, 2, 3, 4, 5};
int index = Arrays.binarySearch(arr, 3);
System.out.println(index); // 출력: 2 (3은 인덱스 2에 위치)
6. Arrays.deepEquals()
2차원 이상의 배열에 대해 배열의 내용이 동일한지 비교할 때 사용한다.
int[][] arr1 = {{1, 2}, {3, 4}};
int[][] arr2 = {{1, 2}, {3, 4}};
System.out.println(Arrays.deepEquals(arr1, arr2)); // ✅ true
배열 사용 시 주의점
- 크기가 고정됨 → 크기를 변경하려면 새 배열을 만들어야 함.
- IndexOutOfBoundsException 발생 가능 → 범위를 초과하는 인덱스 접근 주의.
- 요소 추가 및 삭제 불가 → 추가 및 삭제하려면 List를 사용하는 것이 좋음.
2️⃣ 리스트(List)
배열과 달리 크기가 동적으로 변하는 자료구조다.
ArrayList, LinkedList 등 다양한 구현체가 있으며, 가장 많이 쓰이는 것은 ArrayList다.
리스트 선언 및 초기화
import java.util.ArrayList;
import java.util.List;
List<Integer> list1 = new ArrayList<>(); // Integer 리스트
List<String> list2 = new ArrayList<>(); // String 리스트
// 리스트 초기화
List<Integer> list3 = new ArrayList<>(List.of(1, 2, 3, 4, 5));
리스트 데이터 추가, 접근, 삭제
📌 데이터 추가 (add())
List<Integer> list = new ArrayList<>();
list.add(10); // [10]
list.add(20); // [10, 20]
list.add(1, 15); // [10, 15, 20] (1번 인덱스에 15 삽입)
📌 데이터 접근 (get())
System.out.println(list.get(1)); // ✅ 15
📌 데이터 삭제 (remove())
list.remove(1); // 인덱스 1의 값(15) 삭제 → [10, 20]
list.remove(Integer.valueOf(20)); // 값 20 삭제 → [10]
리스트 크기 확인
System.out.println(list.size()); // ✅ 2
리스트 요소 출력
- 기본 출력
System.out.println(list); // 리스트 출력 → [10, 20]
- String.join() 활용
System.out.println(String.join(", ", list));
- for문
for (int num : list) {
System.out.print(num + " ");
}
리스트 정렬
📌 오름차순 정렬
1. Collections.sort() 사용
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
Collections.sort(list);
System.out.println(list);
// 출력: [1, 2, 3, 5, 8]
2. List.sort() 사용 (Java 8+)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
list.sort(Integer::compareTo);
System.out.println(list);
// 출력: [1, 2, 3, 5, 8]
3. Stream API 사용 (Java 8+)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
list = list.stream().sorted().collect(Collectors.toList());
System.out.println(list);
// 출력: [1, 2, 3, 5, 8]
4. 람다식 사용 (Java 8+)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
list.sort((a, b) -> a - b);
System.out.println(list);
// 출력: [1, 2, 3, 5, 8]
📌 내림차순 정렬
1. Collections.sort() 사용
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
Collections.sort(list, Collections.reverseOrder());
System.out.println(list);
// 출력: [8, 5, 3, 2, 1]
2. List.sort() 사용 (Java 8+)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Comparator;
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
list.sort(Comparator.reverseOrder());
System.out.println(list);
// 출력: [8, 5, 3, 2, 1]
3. Stream API 사용 (Java 8+)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Comparator;
import java.util.stream.Collectors;
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
list = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
System.out.println(list);
// 출력: [8, 5, 3, 2, 1]
4. 람다식 사용 (Java 8+)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
list.sort((a, b) -> b - a);
System.out.println(list);
// 출력: [8, 5, 3, 2, 1]
리스트 유용한 메서드
1. add()
리스트에 요소를 추가하는 가장 기본적인 메서드다.
List<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
System.out.println(list); // 출력: [10, 20]
2. addAll()
리스트에 다수의 요소를 한 번에 추가하는 메서드다.
List<Integer> list = new ArrayList<>();
list.addAll(List.of(1, 2, 3, 4));
System.out.println(list); // 출력: [1, 2, 3, 4]
3. remove()
리스트에서 요소를 삭제할 때 사용한다.
List<Integer> list = new ArrayList<>(List.of(10, 20, 30, 40));
list.remove(Integer.valueOf(20)); // 값 20을 삭제
System.out.println(list); // 출력: [10, 30, 40]
4. set()
리스트에서 특정 인덱스의 값을 수정할 때 사용한다.
List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
list.set(1, 100); // 인덱스 1의 값(2)을 100으로 수정
System.out.println(list); // 출력: [1, 100, 3]
5. get()
리스트에서 특정 인덱스의 값을 가져올 때 사용한다.
List<Integer> list = new ArrayList<>(List.of(10, 20, 30));
System.out.println(list.get(1)); // 출력: 20
6. contains()
리스트에 특정 값이 포함되어 있는지 확인할 때 사용한다.
List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4));
System.out.println(list.contains(3)); // ✅ true
System.out.println(list.contains(5)); // ✅ false
7. indexOf()
리스트에서 특정 값의 인덱스를 찾을 때 사용한다.
List<Integer> list = new ArrayList<>(List.of(10, 20, 30, 40));
System.out.println(list.indexOf(30)); // ✅ 2 (인덱스 2)
8. isEmpty()
리스트가 비어있는지 확인할 때 사용한다.
List<Integer> list = new ArrayList<>();
System.out.println(list.isEmpty()); // ✅ true
list.add(1);
System.out.println(list.isEmpty()); // ✅ false
9. clear()
리스트의 모든 요소를 삭제할 때 사용한다.
List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
list.clear();
System.out.println(list); // 출력: []
10. sort()
리스트의 요소들을 정렬할 때 사용한다.
List<Integer> list = new ArrayList<>(List.of(5, 3, 8, 1));
list.sort(Comparator.naturalOrder()); // 오름차순 정렬
System.out.println(list); // 출력: [1, 3, 5, 8]
11. reverse()
리스트의 요소 순서를 뒤집을 때 사용한다.
import java.util.Collections;
List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
Collections.reverse(list);
System.out.println(list); // 출력: [3, 2, 1]
12. subList()
리스트에서 일부분을 잘라낸 서브리스트를 반환한다.
List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4, 5));
List<Integer> subList = list.subList(1, 4); // 인덱스 1부터 3까지
System.out.println(subList); // 출력: [2, 3, 4]
리스트 사용 시 주의점
- ArrayList는 배열 기반이므로 중간 삽입/삭제가 느림 → LinkedList를 고려.
- remove(int index) vs remove(Object obj) 주의 → 값이 Integer인 경우 remove(index)가 호출될 수 있음.
- NullPointerException 주의 → 리스트가 null이면 메서드 호출 시 예외 발생.
3️⃣ 배열과 리스트 정렬
어떤 정렬 방법을 사용해야 할까?
방법 | 배열(Array) | 리스트(List) | Java 버전 | 성능 |
Arrays.sort() | ✅ 가능 | ❌ 불가능 | Java 1.2+ | 빠름 (Dual-Pivot QuickSort) |
Arrays.sort() | ✅ 가능 | ❌ 불가능 | Java 1.2+ | 빠름 (Dual-Pivot QuickSort) |
Arrays.sort() | ✅ 가능 | ❌ 불가능 | Java 1.2+ | 빠름 (Dual-Pivot QuickSort) |
Arrays.sort() | ✅ 가능 | ❌ 불가능 | Java 1.2+ | 빠름 (Dual-Pivot QuickSort) |
Arrays.sort() | ✅ 가능 | ❌ 불가능 | Java 1.2+ | 빠름 (Dual-Pivot QuickSort) |
📌 배열(Array) 정렬
- 일반적인 정렬 → Arrays.sort()
- 내림차순 정렬 → Arrays.sort(arr, Comparator.reverseOrder())
- Stream API 사용 (배열 변환 포함) → Arrays.stream(arr).sorted()
📌 리스트(List) 정렬
- 일반적인 정렬 → Collections.sort(list)
- 내림차순 정렬 → Collections.sort(list, Collections.reverseOrder())
- Java 8 이상이면? → list.sort(Comparator.naturalOrder())
- Stream API 사용 (메모리 비용 감수 가능하면) → list.stream().sorted()
- 람다식 사용 → list.sort((a, b) -> a - b)
👉 결론
- 배열 정렬은 Arrays.sort()를 가장 많이 사용
- 리스트 정렬은 List.sort()가 가장 직관적
- Stream API는 일회성 정렬이 필요한 경우 사용 (가독성은 좋지만 성능 저하 가능)
- 람다식을 활용하면 코드가 더 간결해짐 (Java 8 이상이라면 적극 활용 가능)
Comparator.reverseOrder() vs Collections.reverseOrder()
📌 Comparator.reverseOrder()
- Comparator의 정적 메서드
- 기본 정렬 순서(오름차순)를 반대로(내림차순) 정렬
- null을 허용하지 않음 (널 값을 비교하면 NullPointerException 발생)
- Comparator.naturalOrder()와 함께 사용 가능
- Java 8부터 사용 가능
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
list.sort(Comparator.reverseOrder()); // 내림차순 정렬
System.out.println(list);
// 출력: [8, 5, 3, 2, 1]
}
}
📌 Collections.reverseOrder()
- Collections의 정적 메서드
- 기본적으로 Comparable을 구현한 객체들만 정렬 가능
- null을 허용하며, null이 포함된 경우에도 예외 없이 동작
- Java 1.2부터 사용 가능 (훨씬 이전부터 존재)
- 기본 정렬 기준(오름차순)을 반대로(내림차순) 뒤집는 Comparator를 반환
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));
list.sort(Collections.reverseOrder()); // 내림차순 정렬
System.out.println(list);
// 출력: [8, 5, 3, 2, 1]
}
}
📌 두 메서드의 차이점 비교
구분 | Comparator.reverseOrder() | Collections.reverseOrder() |
정의 위치 | Comparator 인터페이스의 정적 메서드 | Collections 클래스의 정적 메서드 |
사용 가능 버전 | Java 8 이상 | Java 1.2 이상 |
기본 정렬 기준 | 기본 정렬 순서(오름차순)를 반대로(내림차순) | Comparable을 기준으로 정렬을 반대로(내림차순) |
null 허용 여부 | ❌ null을 허용하지 않음 (예외 발생) | ✅ null을 허용 |
반환 타입 | Comparator<T> | Comparator<T> |
📌 어떤 걸 써야 할까?
- Java 8 이상을 사용하고 null이 포함되지 않은 데이터라면? → Comparator.reverseOrder()를 쓰는 게 더 간결하고 직관적!
- Java 1.2 이상의 모든 버전에서 동작하고, null을 포함한 리스트를 정렬해야 한다면? → Collections.reverseOrder()를 쓰는 게 더 안정적!