직접 정렬 알고리즘을 구현하여 정렬할 수도 있지만, 자바에서 기본적으로 제공하는 정렬 기능을 활용하면 아주 편하게 코드를 작성할 수 있다. 여기저기 자유자재로 쓸 수 있도록 방법을 쫙 정리해보았다.
Arrays.sort()
배열은 Arrays
클래스의 static 메서드 중 sort()
를 사용하여 쉽게 정렬할 수 있다.
static void sort(Object[] a)
배열만을 인자로 넘겨 sort()
를 사용하는 경우, Comparable
interface가 구현된 Object의 natural ordering에 따라 정렬된다. Comparable
interface를 구현하지 않은 객체는 이 메서드를 사용할 수 없으며, Comparable
을 구현하거나 아래의 메서드를 사용해야 한다.
Number, Boolean, Character, String 등 우리가 자주 사용하는 기본 자료형들은 기본적으로 Comparable
을 구현하여 compareTo()
를 통해 오름차순으로 정렬하도록 정의되어 있다. Primitive type역시 오토 박싱 & 언박싱을 통해 정렬이 된다.
int[] arr = {1,3,2,4,5};
Arrays.sort(arr); // [1, 2, 3, 4, 5]
static <T> void sort(T[] a, Comparator<? super T> c)
Comparable
이 구현되지 않은 클래스를 정렬하거나, natural ordering이 아닌 별도의 기준으로 배열을 정렬하려면 두 번째 인자로 Comparator
구현체를 넘겨 사용해야 한다. 직접 구현체를 작성하여 인자로 넘기거나, lambda expression으로 인자를 줘야 하는데, 람다식이 훨씬 가독성이 좋고 간결하기 때문에 현업에서 많이 사용한다. Comparator
에 관한 내용은 여기서 확인할 수 있다.
Integer[] arr = {1, 3, 5, 2, 4};
// 1. Comparator 구현체
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
// 2. Lambda Expression
Arrays.sort(arr, (o1, o2) -> o2 - o1);
static void sort(Object[] a, int fromIndex, int toIndex)
범위를 지정하여 객체를 정렬하고 싶을 때는 index를 인자로 넘겨 정렬할 수 있다. 주어진 배열 a
의 fromIndex
element부터 toIndex - 1
까지의 element를 정렬한다. 즉, toIndex
는 배열하고자 하는 element의 index + 1으로 인자를 넘겨야 한다는 것이다.
int[] arr = {99, 3, 2, 1, 100};
Arrays.sort(arr, 1, 4); // [99, 1, 2, 3, 100]
Collections
& Collection
collection의 element를 정렬하기 위해서는, 우선 대상 타입이 List interface를 구현한 ordered collection 타입이어야 한다. Map이나 Set같이 순서가 없는 타입은 아래의 방식으로는 정렬할 수 없고, LinkedHashMap, SortedMap과 같은 별도의 자료형을 활용해야 한다.
방법은 두 가지가 있는데, Collections
의 static 메서드를 사용하거나, List
interface의 sort()
인스턴스 메서드를 사용하면 된다.
Collections
This class consists exclusively of static methods that operate on or return collections.
설명에서 알 수 있듯이, Collections
는 컬렉션 타입 대상의 여러가지 편리한 동작들을 static 메서드로 모아 놓은 클래스다. 인터페이스가 아니고 클래스이기 때문에 인자로 정렬하고자 하는 컬렉션을 넘겨 사용한다.
static <T extends Comparable<? super T>> void sort(List<T> list)
Comparable
interface가 구현된 클래스의 경우 별도의 Comparator
구현체 없이 바로 list
만 인자로 넘겨 정렬할 수 있다. 사용 예는 아래와 같다.
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(5);
list.add(3);
Collections.sort(list); // [1, 3, 5]
이렇게 list
만 단일 인자로 넘기면, Collections
는 내부적으로 instance 메서드인 list.sort(null)
을 호출하여 정렬한다. list.sort()
에 대한 내용은 아래에서 확인할 수 있다.
static <T> void sort(List<T> list, Comparator<? super T> c)
Comparable
interface를 구현하지 않은 클래스이거나, 구현했더라도 다른 정렬 기준을 부여하고 싶은 경우 두 번째 인자로 Comparator
구현체를 넘겨 element를 정렬할 수 있다. 이 메서드 역시 위의 메서드와 마찬가지로 list.sort()를 내부적으로 호출하여 element를 정렬한다. 사용 예는 아래와 같다.
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(5);
list.add(3);
Collections.sort(list, ((o1, o2) -> o2 - o1)); // [5, 3, 1]
Collection
The root interface in the collection hierarchy. A collection represents a group of objects, known as its elements.
Collection
은 collection 구조의 가장 최상위 interface로서, element를 다루기 위한 여러 인스턴스 메서드를 제공한다. 그 중 ordered collectoin을 의미하는 List
interface에서는 정렬을 위한 instance 메서드를 제공한다.
default void sort(Comparator<? super E> c)
이 List
의 메서드가 Collections
의 static 메서드와 다른 점은 Comparator
구현체를 필수 인자로 넘겨줘야 한다는 것이다.
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(5);
list.add(3);
list.sort(Comparator.naturalOrder()); // [1, 3, 5]
하지만, 인자에 null
을 넘겨 줄 수 있는데, 이 경우 자동으로 Comparable
을 구현한 클래스의 natural ordering으로 정렬된다.
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(5);
list.add(3);
list.sort(null); // [1, 3, 5]
Comparator
Comparator
는 @FuntionalInterface
로, compare()
와 equal()
을 abstract 메서드로 갖고 있다. 주로 compare()
메서드를 구현한 구현체를 정렬 메서드의 인자로 넘겨 기준으로 활용한다. 참고로, Comparator
의 generic type <T>
는 primitive type을 허용하지 않는다.
또한, Comparator
클래스는 몇 가지 유용한 static 메서드를 제공한다. 이들을 활용하면 직접 구현체나 람다식을 작성하지 않고도 보다 간결하게 객체를 정렬할 수 있다.
단 Comparable
이 구현되어 있지 않은 클래스는 직접 Comparator
구현체를 인자로 추가로 넘겨 주지 않는 이상, 아래 메서드들을 활용할 수 없다. natural ordering이 정의되어 있어야 이를 기반으로 변환된 Comparator
을 생성할 수 있기 때문이다.
static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor)
Integer를 오름차순으로 정렬하는 Comparator
구현체를 반환한다. 이 때 정렬에 사용하는 int 값은 인자로 받은 ToIntFunction
에 따라 객체에서 추출된다.
일례로, 아래의 costs
와 같은 int 2차원 배열을 특정 index 값을 기준으로 정렬하려면 comparingInt()
를 이렇게 사용하면 된다.
int[][] costs = {{0,1,4}, {0,2,2}, {1,2,5}};
Arrays.sort(costs, (Comparator.comparingInt(o -> o[2])));
// [[0,2,2], [0,1,4], [1,2,5]]
comparingInt
말고도 double, long 타입도 위의 예시처럼 간단하게 구현 가능하다. 그 외의 자료형 Comparable
interface가 구현되어 있는 경우에 한해 comparing()
을 활용할 수 있다.
static <T extends Comparable<? super T>> Comparator<T> reverseOrder()
Comparable
이 구현된 클래스의 인스턴스를 natural ordering의 reversed, 즉 내림차순으로 정렬하는 Comparator
구현체를 반환한다.
아래와 같이 Comparator
구현체를 인자로 요구하는 메서드에 이를 활용하면 굳이 구현체나 람다식을 작성할 필요 없이 내림차순으로 간단하게 정렬할 수 있다.
Integer[] arr = {99, 3, 5, 1, 1000};
Arrays.sort(arr, Comparator.reverseOrder()); // [1000, 99, 5, 3, 1]
본 글은 Java SE 14 & JDK 14 공식 문서를 기준으로 작성되었습니다.