객체 크기를 비교해 정렬하기 - Comparable과 Comparator 인터페이스

  • Primitive Type(기본형 데이터 타입) : 더 이상 쪼개질 수 없는 원자성의 데이터이므로 그 자체로 크고 작음을 비교할 수 있음
  • Reference Type(참조형 데이터 타입) : 대상을 나타내는 복합체로 크고 작음을 비교하기 어려움

따라서 객체를 비교 가능한 타입으로 만들어줘야 함!

Comparable 인터페이스

  • 객체를 비교 가능한 타입으로 만들기 위해 Comparable<T> 인터페이스 타입으로 형변환 (implements Comparable<T>)
  • Comparable 인터페이스의 compareTo(T o) 메서드를 오버라이드하여 객체에 맞게 구현한 뒤 이 메서드를 호출하여 정렬

compareTo()

  • compareTo 메서드를 통해 sort 진행 시 기본적으로 오름차순 정렬됨
  • compareTo는 객체 자기 자신과 매개변수로 오는 객체의 크기를 비교하므로 매개변수는 자신과 같은 타입의 매개변수 하나만 올 수 있음
  • 리턴타입은 int 타입

    • 음수 : 내가 매개변수 객체보다 크기 작음(원소 자리 교환 X)
    • 0 : 나와 매개변수 객체 크기 같음(원소 자리 교환 X)
    • 양수 : 내가 매개변수 객체보다 크기 큼(원소 자리 교환 O)
Java
public class Student implements Comparable<Student> {
	
    int no, score;

	public Student(int no, int score) {
		super();
		this.no = no;
		this.score = score;
	}

    // 크기 비교 방법 1
	@Override
	public int compareTo(Student o) {
		if (this.no == o.no) return 0;
		return this.no < o.no ? -1 : 1;    // 오름차순 : <, 내림차순 : >
	}
	
    // 크기 비교 방법 2
	@Override
	public int compareTo(Student o) {
		return this.no - o.no;   // 음수와 양수가 섞여있는 상황에서는 언더플로 혹은 오버플로 발생 가능(int형으로 표현하는 범위 초과)
		return Integer.compare(o.no, this.no);   // 언더, 오버플로 방지, 첫번째 매개변수 기준
	}
}

public class ComparableTest {
    public static void main(String[] args){
        Student[] students = {
            new Student(10, 100),
			new Student(3, 50),
			new Student(54, 80),
			new Student(23, 10)
        }
    }
}

// Comparable 인터페이스의 compareTo 메서드 재정의했으므로 에러 발생 X 
Arrays.sort(students);

Comparator 인터페이스

  • 사용자 정의 클래스(데이터 타입)의 경우 Comparable 인터페이스의 compareTo 메서드를 적절하게 재정의하여 입맛에 맞게 정렬할 수 있지만, String 타입과 같은 API로 제공되는 참조형 데이터 타입은 재정의 불가능함
  • 또한 상황에 맞게 정렬하고 싶을때마다 클래스의 메서드를 수정하는 것은 비현실적임
  • 객체를 사용하는 상황에서 조금 더 간편하고 일시적인 방법으로 객체의 크기를 비교하고 정렬하기 위해 Comparator<T> 인터페이스를 구현하여 사용
  • Comparator 인터페이스의 compare(T o1, T o2) 메서드는 내장 클래스로 구현하거나 익명함수, 람다식으로 구현 가능

compare()

  • Comparable 인터페이스의 compareTo 메서드는 자기자신이 스스로와 다른 객체를 비교했다면, Comparator 인터페이스의 compare 메서드는 제 3자에게 비교를 요청하여 결과를 얻음
  • compare 메서드를 통해 sort 진행 시 기본적으로 오름차순 정렬됨
  • compare는 제 3자가 두 개의 객체의 크기를 비교하므로 매개변수로는 같은 타입의 매개변수 2개가 와야 함
  • 리턴타입은 int 타입

    • 음수 : 앞의 객체가 뒤의 객체보다 크기 작음(원소 자리 교환 X)
    • 0 : 앞의 객체와 뒤의 객체 크기 같음(원소 자리 교환 X)
    • 양수 : 앞의 객체가 뒤의 객체보다 크기 큼(원소 자리 교환 O)
Java
public class ComparatorTest {
	
    // 내장 클래스로 구현
	static class StudentComparator implements Comparator<Student> {

		@Override
		public int compare(Student o1, Student o2) {
			return o1.score - o2.score;   // 점수 기준 오름차순
		}
		
	}

	public static void main(String[] args) {
		
		Student[] students = {
				new Student(1, 10),
				new Student(3, 50),
				new Student(2, 80),
				new Student(4, 10)
		};
		
        // 내장 클래스 사용
		Arrays.sort(students, new StudentComparator());   
		// Student가 갖고있는 비교 기능(compareTo)이 적용되지 않고, 별도로 정의한 Comparator가 비교를 하고있음. 클래스에 정의한 compare와 다른 기준으로 정렬하고 싶을 때 커스텀하는 용도로 쓸 수 있음
		
        // 익명 클래스로 구현
		Arrays.sort(students, new Comparator<Student>() {

			@Override
			public int compare(Student o1, Student o2) {
				return o2.score - o1.score;
			}
			
		});
		
        // 람다식으로 구현
		Arrays.sort(students, (o1, o2) -> o2.score - o1.score);
		
        // 기준 2개 비교
		Arrays.sort(students, (o1, o2) -> o2.score != o1.score ? o2.score - o1.score : o2.no - o1.no);

		
		int[][] students2 = {
				{1, 10},
				{3, 50},
				{2, 80},
				{4, 10}
		};
		
        // 2차원 배열 정렬
		Arrays.sort(students2, new Comparator<int[]>() {

			@Override
			public int compare(int[] o1, int[] o2) {
				return o1[0] - o2[0];
			}
			
		});
		
        // 2차원 배열 람다식 정렬
		Arrays.sort(students2, (o1, o2) -> o1[0] - o2[0]);
		
	}
}