본문으로 건너뛰기
Advertisement

11.5 Collections 유틸리티 클래스

java.util.Collections 클래스는 컬렉션을 다루는 정적(static) 유틸리티 메서드들의 모음입니다. 정렬, 검색, 뒤집기, 불변 컬렉션 생성 등 자주 쓰이는 작업들을 간단히 해결해줍니다.

1. 정렬 및 순서 조작

import java.util.*;

List<Integer> numbers = new ArrayList<>(Arrays.asList(5, 2, 8, 1, 9, 3));

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

// 내림차순 정렬 (Comparator 이용)
Collections.sort(numbers, Comparator.reverseOrder());
System.out.println(numbers); // [9, 8, 5, 3, 2, 1]

// 뒤집기
Collections.reverse(numbers);
System.out.println(numbers); // [1, 2, 3, 5, 8, 9]

// 무작위 섞기
Collections.shuffle(numbers);
System.out.println(numbers); // 랜덤 순서
Collections.shuffle(numbers, new Random(42)); // 시드값으로 재현 가능

// 회전 (왼쪽으로 2칸 이동)
List<String> letters = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E"));
Collections.rotate(letters, -2); // 왼쪽으로 2칸 회전
System.out.println(letters); // [C, D, E, A, B]

// 교환
Collections.swap(letters, 0, 4);
System.out.println(letters); // 0번, 4번 요소 교환

2. 검색 및 통계

List<Integer> sorted = Arrays.asList(1, 3, 5, 7, 9, 11);

// 이진 탐색 (정렬된 리스트에서만 사용!)
int idx = Collections.binarySearch(sorted, 7);
System.out.println(idx); // 3 (인덱스)

// 최솟값, 최댓값
System.out.println(Collections.min(sorted)); // 1
System.out.println(Collections.max(sorted)); // 11

// 특정 요소의 출현 횟수
List<String> fruits = Arrays.asList("사과", "바나나", "사과", "딸기", "사과");
System.out.println(Collections.frequency(fruits, "사과")); // 3

// 한 컬렉션이 다른 컬렉션의 부분집합인지
List<String> subset = Arrays.asList("사과", "딸기");
System.out.println(Collections.containsAll(fruits, subset)); // (List에는 없음)
// 직접: fruits.containsAll(subset) → true

3. 불변(Immutable) 컬렉션 생성

외부에서 수정할 수 없는 읽기 전용 컬렉션을 만들 때 사용합니다. 수정 시도 시 UnsupportedOperationException이 발생합니다.

// 기존 컬렉션을 불변으로 감싸기
List<String> mutable = new ArrayList<>(Arrays.asList("A", "B", "C"));
List<String> immutable = Collections.unmodifiableList(mutable);

// immutable.add("D"); // ❌ UnsupportedOperationException!
System.out.println(immutable.get(0)); // "A" (읽기는 OK)

Map<String, Integer> map = new HashMap<>();
map.put("one", 1); map.put("two", 2);
Map<String, Integer> readOnlyMap = Collections.unmodifiableMap(map);

팩토리 메서드 (Java 9+)

더 간결한 불변 컬렉션 생성 방법입니다.

// List.of - 불변 리스트 (null 불가)
List<String> list = List.of("Alice", "Bob", "Charlie");
// list.add("Dave"); // ❌

// Set.of - 불변 집합 (중복 불가, 순서 보장 안 됨)
Set<Integer> set = Set.of(1, 2, 3, 4, 5);

// Map.of - 불변 맵 (키-값 쌍으로 전달)
Map<String, Integer> scoreMap = Map.of(
"Alice", 95,
"Bob", 87,
"Carol", 92
);

// Map.entry + Map.ofEntries (10개 이상)
Map<String, Integer> bigMap = Map.ofEntries(
Map.entry("A", 1),
Map.entry("B", 2),
Map.entry("C", 3)
// ... 최대 제한 없음
);

4. 특수 컬렉션 생성

// 빈 불변 컬렉션 (null 반환 대신 빈 컬렉션 반환 패턴)
List<String> emptyList = Collections.emptyList();
Set<String> emptySet = Collections.emptySet();
Map<String, Integer> emptyMap = Collections.emptyMap();

// 단일 요소 불변 컬렉션
List<String> singleList = Collections.singletonList("only");
Set<String> singleSet = Collections.singleton("only");

// 동일 요소 N번 반복 리스트 (초기화에 유용)
List<Integer> zeros = Collections.nCopies(5, 0);
System.out.println(zeros); // [0, 0, 0, 0, 0]

5. 스레드 안전 컬렉션 생성

// 기존 컬렉션을 스레드 안전하게 감싸기 (레거시 방식)
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());

// ✅ 현대적인 방식 (java.util.concurrent 패키지 사용 권장)
import java.util.concurrent.*;
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();

6. 람다와 함께 쓰는 컬렉션 유틸리티 (Java 8+)

List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie", "Dave"));

// removeIf: 조건에 맞는 요소 제거
names.removeIf(name -> name.length() < 4);
System.out.println(names); // [Alice, Charlie, Dave]

// replaceAll: 모든 요소를 변환
names.replaceAll(String::toUpperCase);
System.out.println(names); // [ALICE, CHARLIE, DAVE]

// sort (List.sort - Comparator 직접 사용)
names.sort(Comparator.naturalOrder()); // 오름차순
names.sort(Comparator.reverseOrder()); // 내림차순
names.sort(Comparator.comparingInt(String::length)); // 길이 순
names.sort(Comparator.comparingInt(String::length)
.reversed()
.thenComparing(Comparator.naturalOrder())); // 길이 내림차순 후 알파벳 순

// Map 유틸리티 메서드 (Java 8+)
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);

scores.getOrDefault("Carol", 0); // Carol 없으면 0
scores.putIfAbsent("Alice", 100); // 이미 있으면 무시
scores.computeIfAbsent("Dave", k -> 75); // Dave 없으면 75 추가
scores.merge("Alice", 5, Integer::sum); // Alice의 점수에 5 더하기
scores.forEach((name, score) ->
System.out.println(name + ": " + score));

고수 팁

불변 컬렉션 vs 방어적 복사:

  • List.of(), Map.of() 등 Java 9+ 팩토리 메서드: 진짜 불변 (수정 시 예외)
  • Collections.unmodifiableList(): 래퍼일 뿐, 원본 컬렉션이 수정되면 같이 변함
List<String> original = new ArrayList<>(List.of("A", "B"));
List<String> unmod = Collections.unmodifiableList(original);

original.add("C"); // 원본 수정
System.out.println(unmod); // [A, B, C] ← 같이 변함!

// 진정한 불변 복사본
List<String> trulyImmutable = List.copyOf(original); // Java 10+

메서드에서 컬렉션 반환 시 null 대신 Collections.emptyList()를 반환하면 NPE 방지 + 코드 간결화가 됩니다.

Advertisement