Java Collections 유틸리티 클래스의 주요 기능 활용
Java 컬렉션 프레임워크의 핵심 인터페이스인 List, Set, Map에 이어 이번에는 java.util.Collections 클래스를 살펴보겠습니다. Collection 인터페이스와 혼동하지 않도록 주의해야 하며, Collections는 다양한 컬렉션 작업을 위한 정적 메서드들을 제공하는 유틸리티 클래스입니다.
1. 컬렉션 정렬 기능
데이터 정렬은 실제 개발에서 자주 요구되는 작업으로, Collections.sort() 메서드를 사용하면 List 컬렉션을 쉽게 정렬할 수 있습니다.
1.1 기본 정렬
List 요소가 Comparable 인터페이스를 구현하고 있다면(예: Integer, String 등), sort 메서드는 해당 객체들의 기본 순서에 따라 정렬합니다.
package com.example.collectionsdemo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SortingExample {
public static void main(String[] args) {
List<Integer> values = new ArrayList<>();
values.add(8);
values.add(2);
values.add(7);
values.add(4);
System.out.println("정렬 전: " + values);
Collections.sort(values);
System.out.println("정렬 후: " + values);
}
}
1.2 사용자 정의 정렬
사용자 정의 객체(예: Person)에 대해 정렬하려면 Comparator를 사용하여 원하는 정렬 규칙을 정의할 수 있습니다.
package com.example.collectionsdemo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CustomSortingExample {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("홍길동", 25));
people.add(new Person("김철수", 30));
// 람다 표현식 사용
Collections.sort(people, (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()));
// Comparator 유틸리티 메서드 사용
Collections.sort(people, java.util.Comparator.comparingInt(Person::getAge));
System.out.println("나이순 정렬 결과: " + people);
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() { return age; }
public String getName() { return name; }
@Override
public String toString() {
return name + "(" + age + "세)";
}
}
2. 이진 검색 기능
이미 정렬된 List에 대해 binarySearch 메서드를 사용하면 이진 검색 알고리즘을 통해 효율적으로 데이터를 찾을 수 있습니다.
성능 비교
package com.example.collectionsdemo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SearchPerformanceTest {
public static void main(String[] args) {
List<Integer> dataList = new ArrayList<>();
for (int i = 0; i < 500000; i++) {
dataList.add(i);
}
int searchTarget = 456789;
// 선형 검색
long startTime1 = System.nanoTime();
linearSearch(dataList, searchTarget);
long endTime1 = System.nanoTime();
System.out.println("선형 검색 소요 시간: " + (endTime1 - startTime1) / 1_000_000.0 + " ms");
// 이진 검색
long startTime2 = System.nanoTime();
Collections.binarySearch(dataList, searchTarget);
long endTime2 = System.nanoTime();
System.out.println("이진 검색 소요 시간: " + (endTime2 - startTime2) / 1_000_000.0 + " ms");
}
private static void linearSearch(List<Integer> list, int target) {
for (Integer value : list) {
if (value.equals(target)) {
break;
}
}
}
}
이진 검색은 매번 절반의 데이터를 제외시키므로 대규모 데이터셋에서 선형 검색보다 훨씬 빠릅니다. 단, 검색 전 반드시 정렬 상태여야 합니다.
3. 스레드 안전한 컬렉션 생성
ArrayList, HashMap 등은 기본적으로 스레드 안전하지 않으므로 멀티스레드 환경에서는 문제가 발생할 수 있습니다. Collections는 synchronizedXXX() 메서드를 제공하여 기존 컬렉션을 스레드 안전하게 포장할 수 있습니다.
package com.example.collectionsdemo;
import java.util.*;
public class ThreadSafeCollectionExample {
public static void main(String[] args) {
List<String> baseList = new ArrayList<>();
List<String> threadSafeList = Collections.synchronizedList(baseList);
threadSafeList.add("안전한 추가");
// Set과 Map도 동일한 방식으로 사용 가능
// Set<String> safeSet = Collections.synchronizedSet(new HashSet<>());
// Map<String, Integer> safeMap = Collections.synchronizedMap(new HashMap<>());
}
}
이 방식은 각각의 단일 메서드 호출(add, get 등)은 원자성을 보장하지만 복합 연산의 경우 여전히 별도의 동기화가 필요합니다.
4. 불변 컬렉션 및 빈 컬렉션 생성
불변 컬렉션
수정이 불가능한 컬렉션이 필요한 경우 unmodifiableXXX() 메서드를 사용할 수 있습니다.
package com.example.collectionsdemo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ImmutableCollectionExample {
public static void main(String[] args) {
List<String> mutableList = new ArrayList<>();
mutableList.add("수정 가능");
List<String> immutableList = Collections.unmodifiableList(mutableList);
// 아래 코드는 UnsupportedOperationException 발생
// immutableList.add("수정 불가");
}
}
Java 9부터는 List, Set, Map 인터페이스 자체에 of() 정적 메서드가 추가되어 진정한 불변 컬렉션을 더 쉽게 생성할 수 있습니다.
빈 컬렉션
null 반환 대신 빈 컬렉션을 반환하면 NullPointerException 위험을 줄일 수 있습니다.
package com.example.collectionsdemo;
import java.util.Collections;
import java.util.List;
public class EmptyCollectionExample {
public static List<String> retrieveItems(String criteria) {
// 검색 조건에 따라 항목을 찾는 로직
// 결과가 없는 경우
return Collections.emptyList();
}
}
Collections.emptyList()는 매번 새로운 인스턴스를 생성하지 않고 동일한 정적 인스턴스를 반환하여 메모리 효율을 높입니다.
5. 기타 유용한 기능
Collections 클래스는 다음과 같은 다양한 유틸리티 메서드를 제공합니다:
- reverse(List<?> list): 리스트 요소 순서 반전
- shuffle(List<?> list): 리스트 요소 무작위 섞기
- max(Collection<?> coll) / min(Collection<?> coll): 최대/최소 요소 찾기
- fill(List<?> list, T obj): 모든 요소를 지정된 값으로 채우기