안녕하세요! 오늘은 자바의 컬렉션 프레임워크에서 중요한 부분인 정렬과 Collections 유틸리티에 대해 자세히 알아보겠습니다. 이 개념들은 자바 개발자라면 반드시 알아야 할 기본적인 내용이면서도, 프로젝트에서 자주 활용되는 핵심 요소입니다.
목차
- Comparable과 Comparator
- 정렬 로직의 이해
- Collections 유틸리티 클래스
- 가변 vs 불변 컬렉션
- 실무에서의 자료구조 선택 가이드
- 실전 코드 예제
1. Comparable과 Comparator
객체를 정렬하려면 해당 객체들을 비교할 수 있어야 합니다. 자바에서는 이를 위해 두 가지 인터페이스를 제공합니다.
Comparable 인터페이스
Comparable은 객체의 "자연적인 순서(Natural Ordering)"를 정의합니다. 클래스가 이 인터페이스를 구현하면 객체들을 정렬할 수 있는 기본적인 방법을 제공하게 됩니다.
public class MyUser implements Comparable<MyUser> {
private String id;
private int age;
// 생성자 및 getter 생략
@Override
public int compareTo(MyUser o) {
// 나이 기준 오름차순 정렬
return this.age < o.age ? -1 : (this.age == o.age ? 0 : 1);
}
}
Comparator 인터페이스
정렬 방식을 지정할 필요가 있을 때는 Arrays.sort() 또는 Collections.sort()의 인수로 Comparator를 넘기면 됩니다. 이 경우 Comparator가 정렬 우선권을 가집니다.
public class IdComparator implements Comparator<MyUser> {
@Override
public int compare(MyUser o1, MyUser o2) {
return o1.getId().compareTo(o2.getId());
}
}
// 사용 예시
Arrays.sort(users, new IdComparator());
만약 Comparable과 Comparator 둘 다 구현되지 않으면, 비교자에 대해 예외가 발생하여 비교 기능 수행이 불가능합니다.
2. 정렬 로직의 이해
정렬 로직은 현재 객체와 비교 대상 객체의 값을 비교할 때 작동 방식을 이해해야 합니다.
정렬 결과 해석
- 현재 객체 값 < 비교 객체 값: 음수(-) 반환 → 현재 객체가 앞에 배치
- 현재 객체 값 == 비교 객체 값: 0 반환 → 동일한 위치
- 현재 객체 값 > 비교 객체 값: 양수(+) 반환 → 현재 객체가 뒤에 배치
이에 따라 오름차순으로 정렬됩니다. 만약 내림차순 정렬을 원한다면 결과값에 -1을 곱하거나 비교 순서를 바꾸면 됩니다.
Enum 타입 정렬
Enum 타입은 ordinal() 메서드를 통해 선언된 순서값을 반환합니다. 이를 활용하여 Enum 타입도 정렬할 수 있습니다.
아래 카드 게임 예제에서 카드의 정렬 로직을 살펴봅시다:
@Override
public int compareTo(Card o) {
// 숫자가 다르면 숫자 기준 정렬
if (this.cardNumber != o.getCardNumber()) {
return this.cardNumber - o.getCardNumber();
}
// 숫자가 같으면 모양(suit) 기준 정렬
return this.suit.ordinal() - o.suit.ordinal();
}
3. Collections 유틸리티 클래스
Collections 클래스는 컬렉션을 다루는 다양한 정적 메서드를 제공합니다.
주요 메서드
- Shuffle: 랜덤으로 섞음
- Sort: 정렬
- Reverse: 기존 컬렉션을 반대로 정렬해서 반환
- Of: 불변으로 컬렉션 생성!
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
// 최대/최소값 찾기
Integer max = Collections.max(list);
Integer min = Collections.min(list);
// 리스트 섞기
Collections.shuffle(list);
// 정렬
Collections.sort(list);
// 역순 정렬
Collections.reverse(list);
4. 가변 vs 불변 컬렉션
자바에서는 가변(mutable)과 불변(immutable) 컬렉션을 모두 지원합니다.
불변 컬렉션 만들기
가변에서 불변은 Collections.unmodifiable... 메서드를 이용해 가능하며, 가변으로의 전환은 해당 타입의 객체를 새로 만들어 불변의 값을 복사하여 값을 사용할 수 있습니다.
// 불변 리스트 생성
List<Integer> immutableList = List.of(1, 2, 3);
// 가변 리스트로 변환
ArrayList<Integer> mutableList = new ArrayList<>(immutableList);
mutableList.add(4); // 가능
// 다시 불변으로 변환
List<Integer> unmodifiableList = Collections.unmodifiableList(mutableList);
// unmodifiableList.add(5); // 예외 발생
빈 리스트 생성
null 반환보다 빈 리스트 반환이 좋은 상황에 사용합니다.
// 불변 빈 리스트
List<Object> emptyList = Collections.emptyList();
List<Object> emptyList2 = List.of();
5. 실무에서의 자료구조 선택 가이드
자료구조 선택은 매우 중요합니다. 상황에 맞는 자료구조를 선택하면 성능과 가독성을 모두 얻을 수 있습니다.
자료구조 선택 가이드
- 순서가 중요하고 중복이 허용될 때: List > ArrayList
- 중복이 허용되지 않고 순서가 중요하지 않을 때: Set > HashSet
- 키-값 쌍으로 데이터를 저장할 때: Map > HashMap
- FIFO(선입선출) 구조가 필요할 때: Queue > ArrayDeque
순서가 중요하고 중복이 허용될 때는 List가 적합하고, 대량의 데이터 삽입과 추가가 빈번하다면 LinkedList가 더 적합합니다. 단, 일반적인 경우는 ArrayList가 더 좋은 선택입니다.
6. 실전 코드 예제
지금까지 배운 내용을 실제 코드에 적용해보겠습니다.
카드 게임 예제 (Comparable 구현)
public class Card implements Comparable<Card> {
private CardSignature suit = drawRandomCardSuit();
private int cardNumber = drawRandomCardNumber();
// 생략...
@Override
public int compareTo(Card o) {
// 카드 번호가 다르면 번호 기준 정렬
if (this.cardNumber != o.getCardNumber()) {
return this.cardNumber - o.getCardNumber();
}
// 카드 번호가 같으면 카드 모양 기준 정렬
return this.suit.ordinal() - o.suit.ordinal();
}
@Override
public String toString() {
return cardNumber + "(" + suit.symbol + ")";
}
private enum CardSignature {
SPADE("\u2660"), HEART("\u2665"), DIAMOND("\u2666"), CLOVER("\u2663");
private String symbol;
CardSignature(String symbol) {
this.symbol = symbol;
}
public String getSymbol() {
return symbol;
}
}
}
Collections 클래스 활용 예제
public class CollectionsSortMain {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
Integer max = Collections.max(list);
Integer min = Collections.min(list);
System.out.println("max = " + max);
System.out.println("min = " + min);
System.out.println("list = " + list);
Collections.shuffle(list);
System.out.println("Shuffle list = " + list);
Collections.sort(list);
System.out.println("Sort list = " + list);
Collections.reverse(list);
System.out.println("reverse list = " + list);
}
}
불변 컬렉션 예제
public class ImmutableMain {
public static void main(String[] args) {
// 불변 리스트 생성
List<Integer> list = List.of(1, 2, 3);
// 가변 리스트
ArrayList<Integer> mutableList = new ArrayList<>(list);
mutableList.add(4);
System.out.println("mutableList = " + mutableList);
System.out.println("mutableList Class = " + mutableList.getClass());
// 불변 리스트
Collection<Integer> unmodifiableList = Collections.unmodifiableCollection(mutableList);
System.out.println("unmodifiableList Class = " + unmodifiableList.getClass());
}
}
Comparator 활용 예제
public class SortMain3 {
public static void main(String[] args) {
MyUser myUser1 = new MyUser("a", 30);
MyUser myUser2 = new MyUser("b", 20);
MyUser myUser3 = new MyUser("c", 10);
MyUser[] array = {myUser1, myUser2, myUser3};
System.out.println("기본 데이터");
System.out.println(Arrays.toString(array));
System.out.println("Comparable 기본 정렬");
Arrays.sort(array); // MyUser의 compareTo 메서드 사용
System.out.println(Arrays.toString(array));
System.out.println("IdComparator 정렬");
Arrays.sort(array, new IdComparator());
System.out.println(Arrays.toString(array));
System.out.println("IdComparator().reversed() 정렬");
Arrays.sort(array, new IdComparator().reversed());
System.out.println(Arrays.toString(array));
}
}
결론
자바의 컬렉션 프레임워크는 객체 정렬을 위한 Comparable과 Comparator 인터페이스, 그리고 다양한 컬렉션 조작을 위한 Collections 유틸리티 클래스를 제공합니다. 이러한 기능들을 잘 활용하면 더 효율적이고 가독성 높은 코드를 작성할 수 있습니다.
정렬은 내가 커스텀 작성이 가능하므로, 정렬 로직을 내가 구현해야 합니다. 기본 정렬 메서드를 오버라이드 하면 Comparable의 기본 정렬을 사용하고, 별도의 정렬 방식이 필요할 때는 Comparator를 활용하면 됩니다.
여러분도 이 개념들을 활용해 보셨나요? 어떤 경우에 어떤 방식의 정렬이 더 효과적이었는지, 혹은 어떤 자료구조가 특정 상황에서 더 유용했는지 경험을 공유해주세요! 함께 배우고 성장하는 개발자 커뮤니티를 만들어가요! 😊
'📚 학습 기록 > Java 기초 & 중급' 카테고리의 다른 글
자바 컬렉션 프레임워크의 순회와 정렬 기초 (1) | 2025.04.30 |
---|---|
자바 컬렉션 프레임워크 실전 가이드: Map과 Deque의 숨겨진 활용법 (0) | 2025.04.30 |
자바 컬렉션 프레임워크 완전정복: Map, Stack, Queue 및 Deque (2) | 2025.04.29 |
자바 Set 컬렉션 완전 정복: HashSet, LinkedHashSet, TreeSet 비교와 활용 (1) | 2025.04.24 |
자바 HashSet 완벽 가이드: equals()와 hashCode()의 중요성과 제네릭 활용법 (1) | 2025.04.22 |