
Java로 코딩 테스트를 치다 보면 가끔 정렬을 요구할 때가 있다.
한 가지 조건에 대한 정렬은
Collections.sort()나
Collections.sort(target, Collections.reverseOrder()) 로 해결할 수 있다.
그러나 여러 가지 조건이 동시에 등장하면 어질어질해진다..
이럴 땐 단순한 정렬로는 해결할 수 없기 때문에
이번 글에서는 복잡한 조건 정렬까지 정리해보려고 한다!
기본적인 것부터 알아보자.
숫자, 문자열 등 기본 타입은 Collections.sort()만 써도 된다.
List<Integer> list = Arrays.asList(3, 1, 4, 1, 2);
Collections.sort(list);
출력해보면 이렇다.

List<Integer> list = Arrays.asList(3, 1, 4, 1, 2);
Collections.sort(list, Collections.reverseOrder());
출력해보면 이렇다.

두 가지 이상의 조건에 대해서 정렬을 할 때는 Comparator에 람다식을 사용해야 한다.
람다식(Lambda Expression)이란? 함수를 간단하게 표현하는 방법이다.
특히, 메서드를 한 줄로 간단히 작성할 때 주로 사용한다.
이런 형태로 쓴다.(매개변수) -> { 실행할 코드 }
여러 조건을 적용해서 정렬하려면, 기준을 우리가 직접 만들어야 한다.
이때 사용하는 것이 바로 Comparator다.
Comparator는
정렬 기준을 직접 정의할 수 있게 도와주는 인터페이스이다.
Java에서는 기본 정렬(한 가지 조건에 대한 오름차순이나 내림차순) 외에도,
여러 조건을 따져서 정렬하고 싶을 때
Comparator를 사용해 기준을 직접 설정할 수 있다.
이때 람다식을 사용한다.
Comparator에서 사용할 때는 람다식을 두 번째 인자에 적어줘야 한다.
Collections.sort(list, (a, b) -> {
if (조건1(a) != 조건1(b)) {
return 조건1(b) - 조건1(a); // 내림차순
} else if (조건2(a) != 조건2(b)) {
return 조건2(a) - 조건2(b); // 오름차순
}
return 최종 비교 결과;
});
문법은 이렇다.
조건 부분에 a와 b에 대해서 비교할 조건을 같지 않다는 조건으로 적어주고,
반환값에서
a - b라고 하면 오름차순,b - a라고 하면 내림차순이 된다.이걸로 봐서는 엥?
할 수도 있으니 문제를 하나 풀어보면서 예시 코드를 보자!
20920번: 영단어 암기는 괴로워 이 문제를 풀려면 여러 가지의 조건으로 정렬해야한다.
그래서 이 문제에 맞게 코드를 한 번 작성해보겠다!
먼저 정렬을 하기 전에 자료구조를 살펴보자.
단어를 입력 받을 때 마다 이렇게 단어를 입력 받은 횟수를 wordCount라는 해쉬맵에 카운트해서 저장했다.

그리고 이것을 정렬과 출력할 때 사용하기 위해 words라는 리스트를 만들어서 키 값을 따로 옮겨담았다.

이제 문제의 세 가지 조건을 차례로 정렬 기준에 추가해보자.
첫 번째로 등장 횟수, 그 다음 길이, 마지막으로 알파벳 순서다.
words리스트를 정렬할 때 wordCount해쉬 맵의 카운트 값을 사용할 것이고,
정렬된 순서대로 words를 출력하면 된다.
그럼 정렬대상을 words로 선택하고,
이 값을 순회하면서 wordCount의 키값으로 사용해서 정렬 조건을 작성하면 된다.
문제에서는 첫 번째 조건으로
1. 자주 나오는 단어일수록 앞에 배치한다.라고 했다.
wordCount에 저장된 value 값으로 비교를 통해서 정렬하면 된다.
자주 나온 순서대로 먼저 출력하라고 했으니, value값을 내림차순으로 정렬하면 된다.
Collections.sort(list, (a, b) -> {
if (조건1(a) != 조건1(b)) {
return 조건1(b) - 조건1(a); // 내림차순
}
내림차순은 이렇게 하면 된다고 했다.
이거에 맞게 코드를 작성해보자.
Collections.sort(words, (a, b) -> {
if (wordCount.get(a) != wordCount.get(b)) {
return wordCount.get(b) - wordCount.get(a);
}
});
물론 여기까지만 하면 else에 대한 반환값을 적어주지 않았기에 경고가 뜬다.
정렬 조건을 추가할 거니까 일단은 마저 작성해보자.
두 번째 정렬 조건은 2. 해당 단어의 길이가 길수록 앞에 배치한다. 이다.
이번에도 내림차순이기에 a와 b의 길이에 대해서 이렇게 작성하면 된다.
Collections.sort(words, (a, b) -> {
if (wordCount.get(a) != wordCount.get(b)) {
return wordCount.get(b) - wordCount.get(a);
} else if (a.length() != b.length()) {
return b.length() - a.length();
}
});
세 번째 정렬 조건은 3. 알파벳 사전 순으로 앞에 있는 단어일수록 앞에 배치한다. 이다.
앞선 두 조건(등장 횟수, 길이)이 모두 같을 때,
최종적으로 알파벳 순으로 비교해야 한다.
Java에서는 문자열을 비교할 때
String.compareTo() 메서드를 사용한다.
a.compareTo(b): 오름차순 정렬b.compareTo(a): 내림차순 정렬따라서 마지막에는 오름차순에 해당하는 a.compareTo(b)를 반환해주면 된다.
그래서 최종 코드는 이렇다.
Collections.sort(words, (a, b) -> {
if (wordCount.get(a) != wordCount.get(b)) {
return wordCount.get(b) - wordCount.get(a); // 등장 횟수 내림차순
} else if (a.length() != b.length()) {
return b.length() - a.length(); // 길이 내림차순
} else {
return a.compareTo(b); // 알파벳 오름차순
}
});
이제 정렬된 리스트를 순서대로 출력하면, 문제에서 요구한 단어장 순서를 완성할 수 있다!
이렇게 두 가지 이상의 조건에 대해서 정렬할 때 Comparator를 사용하면,
문제에서 요구하는 단어장 정렬을 정확히 구현할 수 있다.
다음부터 복잡한 조건 정렬이 나와도 겁먹지 말고,
필요한 기준을 하나씩 차근차근 작성해보자!