Java에서 배열 정렬은 개발자들이 일상적으로 마주하는 작업 중 하나다. Arrays.sort(T[] array, Comparator<? super T> c) 메소드는 사용자 정의 객체 배열을 정렬할 때 사용하는 강력한 도구다. 이 메소드의 내부 동작 원리와 활용 방식을 살펴보자.
1. 기본 사용법
Comparator 인터페이스를 구현하면 객체 간 비교 로직을 정의할 수 있다. 다음 예제는Animal 클래스를 size 필드를 기준으로 정렬하는 방법을 보여준다.
import java.util.Arrays;
import java.util.Comparator;
class Animal {
int size;
public Animal(int s) {
size = s;
}
}
class SizeComparator implements Comparator<Animal> {
@Override
public int compare(Animal a1, Animal a2) {
return a1.size - a2.size;
}
}
public class SortExample {
public static void main(String[] args) {
Animal a1 = new Animal(5);
Animal a2 = new Animal(2);
Animal a3 = new Animal(8);
Animal[] animals = {a1, a2, a3};
printAnimals(animals);
Arrays.sort(animals, new SizeComparator());
printAnimals(animals);
}
public static void printAnimals(Animal[] arr) {
for (Animal a : arr) {
System.out.print(a.size + " ");
}
System.out.println();
}
}
실행 결과:
5 2 8
2 5 8
2. 전략 패턴의 적용
전략 패턴은 런타임 시점에 다양한 알고리즘을 선택할 수 있게 해주는 설계 패턴이다. 정렬에서도 다양한 비교 기준을 적용할 수 있다는 점에서 전략 패턴의 적절한 활용 사례다.
예를 들어,Animal 클래스에 weight 필드가 추가된 상황을 가정해보자. size 기준과 weight 기준,两 가지 다른 비교 전략을 사용할 수 있다.
import java.util.Arrays;
import java.util.Comparator;
class Animal {
int size;
int weight;
public Animal(int s, int w) {
size = s;
weight = w;
}
}
class SizeComparator implements Comparator<Animal> {
@Override
public int compare(Animal a1, Animal a2) {
return a1.size - a2.size;
}
}
class WeightComparator implements Comparator<Animal> {
@Override
public int compare(Animal a1, Animal a2) {
return a1.weight - a2.weight;
}
}
public class StrategyPattern {
public static void main(String[] args) {
Animal a1 = new Animal(3, 30);
Animal a2 = new Animal(1, 50);
Animal a3 = new Animal(2, 20);
Animal[] animals = {a1, a2, a3};
System.out.println("정렬 전:");
printAnimals(animals);
Arrays.sort(animals, new SizeComparator());
System.out.println("size 기준 정렬:");
printAnimals(animals);
Arrays.sort(animals, new WeightComparator());
System.out.println("weight 기준 정렬:");
printAnimals(animals);
}
public static void printAnimals(Animal[] arr) {
for (Animal a : arr) {
System.out.print("size=" + a.size + " weight=" + a.weight + " ");
}
System.out.println();
}
}
실행 결과:
정렬 전:
size=3 weight=30 size=1 weight=50 size=2 weight=20
size 기준 정렬:
size=1 weight=50 size=2 weight=20 size=3 weight=30
weight 기준 정렬:
size=2 weight=20 size=3 weight=30 size=1 weight=50
3. 제네릭 타입 파라미터의 'super' 의미
메소드 시그니처에서 Comparator<? super T>를 사용하는 이유에 대해探讨해보자. 여기서 ? super T는 T 또는 T의 조상 타입을 의미한다.
이러한 설계가 유용한 이유는 동일한 Comparator를 여러 서브 클래스에서 재사용할 수 있기 때문이다.Animal을 상속받은 Cat과 Dog가 있을 때, Animal용 Comparator를 그대로 사용할 수 있다.
import java.util.Arrays;
import java.util.Comparator;
class Animal {
int size;
public Animal(int s) {
size = s;
}
}
class Cat extends Animal {
public Cat(int s) {
super(s);
}
}
class Dog extends Animal {
public Dog(int s) {
super(s);
}
}
class AnimalComparator implements Comparator<Animal> {
@Override
public int compare(Animal a1, Animal a2) {
return a1.size - a2.size;
}
}
public class SuperTypeDemo {
public static void main(String[] args) {
Dog d1 = new Dog(4);
Dog d2 = new Dog(1);
Dog d3 = new Dog(3);
Dog[] dogs = {d1, d2, d3};
printAnimals(dogs);
Arrays.sort(dogs, new AnimalComparator());
printAnimals(dogs);
System.out.println();
Cat c1 = new Cat(4);
Cat c2 = new Cat(1);
Cat c3 = new Cat(3);
Cat[] cats = {c1, c2, c3};
printAnimals(cats);
Arrays.sort(cats, new AnimalComparator());
printAnimals(cats);
}
public static void printAnimals(Animal[] arr) {
for (Animal a : arr) {
System.out.print("size=" + a.size + " ");
}
System.out.println();
}
}
실행 결과:
size=4 size=1 size=3
size=1 size=3 size=4
size=4 size=1 size=3
size=1 size=3 size=4
4. 핵심 정리
Arrays.sort()를 효과적으로 활용하기 위해 기억해야 할 핵심 포인트:
- 제네릭 와일드카드: ? super T를 사용하면 상위 타입의 Comparator를 재사용할 수 있어 유연성이 높아진다
- 전략 패턴: 다양한 비교 알고리즘을 런타임에 선택할 수 있다
- 알고리즘: Java의 정렬 구현은 병합 정렬 기반이며 O(n log n) 시간 복잡도를 가진다
- 유사 메소드:
Collections.sort(List<T> list, Comparator<? super T> c)도 동일한 원리로 동작한다