이 문서는 자바를 공부하면서 가장 헷갈렸던 부분, 정리되지 않았던 부분인 연속 자료형의 정렬에 대한 정리 문서이다.
자바에서 정렬을 할 때에는 크게 두 가지 방법이 있다.
둘 다 정렬할 때 쓰이지만 적용 대상과 내부 동작에서 차이가 있다.
public static void main(String[] args) {
int[] arry = {5, 3, 1, 4, 2};
Arrays.sort(arry); // 기본 오름차순 정렬
System.out.println(Arrays.toString(arry)); // [1, 2, 3, 4, 5]
Arrays.sort(arry, Comparator.reverseOrder()); // Comparator 내림차순 정렬
System.out.println(Arrays.toString(arry)); // [5, 4, 3, 2, 1]
}
⇒ Arrays.sort()에서도 Comparator가 사용 가능하다.
public static void main(String[] args) {
List<Integer> list = Arrays.asList(5, 3, 1, 4, 2);
Collections.sort(list);
System.out.println(Arrays.toString(list)); // [1, 2, 3, 4 ,5]
Collections.sort(list, Comparator.reverseOrder()); // Comparator 내림차순 정렬
System.out.println(Arryas.toString(list)); // [5, 4, 3, 2, 1]
}
정리
| 적용 대상 | 배열(Array) | 리스트(List) |
|---|---|---|
| 소속 클래스 | java.util.Arrays | java.util.Collections |
| 반환 값 | 없음 (원본 배열 수정) | 없음 (원본 리스트 수정) |
| 지원 타입 | 기본형 배열(int[], String[] 등), 객체 배열 | 객체 타입 리스트(List) |
| 정렬 기준 | 기본 오름차순, Comparator 가능 | 기본 오름차순, Comparator 가능 |
참고문서) [Java 기초] Arrays.sort() vs Collections.sort() 차이 정리
자바에서 컬렉션을 정렬하는 방법에는 두 가지가 있다.
Comparable 인터페이스에 compareTo 구현
Comparator 인터페이스에 compare 구현
Comparable 인터페이스는 객체 자체에 기본적인 정렬 기준을 설정하는 인터페이스다. 해당 클래스가 Comparable<클래스명>을 implements하고, compareTo() 메서드를 Override하면 기본 정렬 기준을 정의할 수 있다.
public class Student implements Comparable<Student> {
private int StudentNo;
private String name;
private String number;
private int age;
private String major;
// 생성자 및 getter, setter
@Override
public int compareTo(Student o) {
return this.getStudentNo() - o.getStudentNo(); // 학번 오름차순 정렬
}
}
Comparable<>을 implements해야 하는 이유?
⇒ Integer, Double과 같은 자바가 기존에 Comparable을 구현해놓은 Wrapper 클래스가 아닌 사용자가 직접 생성한 클래스 객체는 컴퓨터가 어떤 멤버 변수를 기준으로 정렬할 지 모르기 때문에 사용자가 직접 Comparable 인터페이스를 implements해서 compareTo() 메서드에 클래스 정렬 기준을 명시해야 한다.
⇒ Collections.sort()의 내부 구조를 보면 인자로 받는 리스트의 요소들이 반드시 Comparable<>을 구현하고 있어야 한다고 정의되어 있다.
Comparator 인터페이스는 클래스에 정렬 기준을 고정시키지 않고 필요할 때마다 원하는 정렬 기준을 동적으로 적용할 수 있다. Comparator을 사용하면 다양한 방식으로 객체를 정렬할 수 있다.
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
// 리스트 요소 추가(생략)
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
if(o1.getMajor().equals(o2.getMajor())
return o2.getAge() - o1.getAge(); // 나이 내림차순 정렬
return o1.getMajor().compareTo(o2.getMarjo());
}
}
}
클래스에서 Comparable<>의 compareTo()를 오버라이딩 했는데 main에서 Comparator<>의 compare()을 다시 오버라이딩한 경우
⇒ compareTo() 메서드는 클래스 내부에 그대로 유지되지만, Collections.sort()의 두 번째 인자에 Comparator을 넘기면, 자바는 compareTo()를 무시하고 인자로 넘겨받은 compare()의 규칙을 최우선으로 적용함
Comparable<>의 compareTo() : 객체의 기본 정렬 기준 (하나만 설정 가능)
Comparator<>의 compare() : 객체의 특별 정렬 기준
return 방식
@Override
public int compare(Student o1, Student o2) {
return o1.getStudentNo() - getStudentNo();
}
@Override
public int compare(Student o1, Student o2) {
return Integer.compare(o1.getStudentNo(), o2.getStudentNo());
}
⇒ o1.x - o2.x 방식을 사용하면 정수 오버플로우가 발생한 경우 다른 결과를 도출하기 때문에 Integer.compare() 방식이 안전하다
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o.getName());
}
⇒ 문자열은 숫자 타입이 아니기 때문에 빼기 연산(-)이 불가능하다. 따라서 문자열끼리 비교하기 위해서는 compareTo() 메서드를 사용하면 된다. (기존에 클래스에서 오버라이딩 했던 compareTo() 메서드와는 별개의 Stirng 비교 메서드이다.)
https://velog.io/@faulty337/Java%EC%9D%98-Collection-%EC%A0%95%EB%A0%AC-%EB%B0%A9%EB%B2%95
Collections.sort(list, Comparator.comparingInt(Student::getStudentNo));
Collections.sort(list, Comparator.comparing(Student::getName));
이 방식은 내부적으로 알아서 Integer.compare나 String.compareTo를 호출해주기 때문에 실수할 확률이 줄어든다