본문 바로가기

프로그래밍 언어/Java

Java) Comparable과 Comparator

comparable과 comparator는 별도의 처리 없이 정렬이 가능한 primitive 타입 값들이 아닌 객체를 정렬할 때 사용한다. 구글에 객체 정렬하는 방법 같은걸 검색해서 사용 예제만 보고 사용했기 때문에 정확히는 '비교' 라는 의미가 담긴 이 객체가 왜 정렬할 때 사용되는지 잘 알지 못한 채 사용하고 있었다. 그러다 Stranger's LAB  에서 좋은 글을 발견해서 공부하면서 그 내용을 정리해본다. 


Comparable과 Comparator

Comparable과 Comparator는 모두 인터페이스(interface)이다. 즉, 인터페이스에 선언된 메소드를 반드시 구현해야 한다. 

Comparable 인터페이스에는 compareTo(T o) 메소드를, Comparator 인터페이스에는 compare(T o1, T o2) 메소드를 반드시 구현해야 한다. 물론 Comparator에는 compare 외에도 더 많은 메소드들이 정의되어 있다. 하지만 이 외의 메소드들은 모두 default이거나 static으로 선언되어 있다. 

  • default : 오버라이드(재정의)가 가능한 메소드
  • static : 오버라이드할 수 없는 메소드
  • 그 외의 메소드 : 반드시 구현해야 하는 메소드 

기본적으로 Comparable과 Comparator는 객체를 비교하는데 사용된다. 객체는 여러 개의 속성을 가질 수 있다. 이런 경우 그 객체를 비교할 때 어떤 속성을 기준으로 비교해야 하는지 불분명하기 때문에 객체 비교 기준에 대해 명시할 수 있는 Comparable이나 Comparator가 필요한 것이다. 

차이점

아주 간단히 요약하자면 Comparable는 자기 자신과 매개변수 객체를 비교하는 것이고, Comparator는 두 매개변수 객체를 비교하는 것이다. 그래서 Comparable의 compare메소드는 파라미터가 두 개이고, Comparator의 compareTo메소드는 파라미터가 하나인 것이다.


Comparable

자기 자신과 매개변수 객체를 비교

기본 사용법

public class ClassName implements Comparable<Type> { 
 
	/* code */
    
	// 필수 구현 부분
	@Override
	public int compareTo(Type o) {
		/* 비교 구현 */
	}
}

Comparable 인터페이스를 상속한 클래스는 compareTo메소드를 반드시 구현해야 한다. compareTo 메소드는 해당 메소드가 선언된 클래스 객체 자기 자신과 파라미터로 들어온 o를 비교한다. compareTo 메소드는 int를 반환하는데 특정 객체 속성을 기준으로 비교한 결과

  • 자기 자신이 비교 대상보다 더 큰 경우 양수를
  • 같은 경우 0을
  • 작은 경우 -1(음수) 

를 반환한다. 이 반환된 값은 간단하게 생각하면 (자기 자신의 비교 속성 값 - 비교 대상의 비교 속성 값)을 한 결과를 반환하다고 생각하면 된다. 다음은 참고한 포스팅에서 가져온 구현 예시이다. 

class Student implements Comparable<Student> {
 
	int age;			// 나이
	int classNumber;	// 학급
	
	Student(int age, int classNumber) {
		this.age = age;
		this.classNumber = classNumber;
	}
	
	@Override
	public int compareTo(Student o) {
 
		/*
		 * 만약 자신의 age가 o의 age보다 크다면 양수가 반환 될 것이고,
		 * 같다면 0을, 작다면 음수를 반환할 것이다.
		 */
		return this.age - o.age;
	}
}

주의할 점

이렇게 객체 특정 속성의 두 수의 차를 통해 결과를 음수, 양수, 0으로 반환하는데 뺄셈 과정에서 int 자료형의 범위를 넘어버리는 경우(overflow/underflow)가 발생할 수 있다. 이 점을 고려해서 int 자료형 범위를 넘는 값을 비교해야 하는 경우에는 아래 예시와 같이 비교 두 수의 차를 반환하는 것이 아니라 조금 귀찮지만 대쇼 비교를 통해 특정 값 (-1, 0,1) 등을 반환하는 것이 안전한다. 

...
@Override
public int compareTo(T o){
	if(this.largeNum > o.largeNum) {
        return 1;
    }
    else if(this.largeNum == o.largeNum) {
        return 0;
    }
    else {
        return -1;
    }
}
...

 

 

Comparator

두 매개변수 객체를 비교 

기본 사용법

import java.util.Comparator;	// import 필요
public class ClassName implements Comparator<Type> { 
 
	/*  code */
 
	// 필수 구현 부분
	@Override
	public int compare(Type o1, Type o2) {
		/*  비교 구현 */
	}
}

매개 변수의 차이만 빼면 Comparable과 동일하다. 메소드가 선언된 클래스와는 상관없이 매개변수 두 개를 받아 비교하는 것이다. 그래서 사용법이나 기본적인 구현 방법도 동일하며, 이 경우에도 int 자료형 범위가 넘는 값을 비교하지 않도록 주의해야 한다. 

 

Comparable과 Comparator을 활용한 정렬

Java는 오름차순 정렬을 디폴트로 한다. 즉, Arrays.sort(), Collections.sort()가 모두 오름차순으로 정렬된다. 따라서 Comparable과 Comparator을 활용한 정렬도 마찬가지로 오름차순으로 정렬한다.

두 수 비교에 따른 작동 방식

A와 B를 비교하여 정렬하는 상황이라고 가정해보자. compare 또는 compareTo를 사용하여 객체를 비교한 후 양수가 나오면 (A-B>0) A가 B보다 크다는 것을 의미한다. 그러므로 오름차순을 기준으로 A, B가 아닌 B, A가 되어야 한다. 즉, 비교 결과 양수가 나오면 두 수의 위치를 swap 해준다. 반면 음수가 나오면 (A-B<0) A가 B보다 작다는 것을 의미하므로 A, B 순서가 오름차순 정렬에 맞기 때문에 두 수의 위치 교환을 하지 않는다. 

정렬 방법

Comparable이나 Comparator 인터페이스를 상속해서 반드시 구현해야 하는 compare나 compareTo 메소드를 구현해 두면 해당 클래스 객체들은 비교 기준이 명확해진다. 이로써 Arrays.sort() 또는 Collections.sort()으로 정렬 메소드를 사용할 수 있다. Comparable이나 Comparator 인터페이스를 상속해서 반드시 구현해야 하는 compare나 compareTo 메소드를 구현하지 않은 경우에는 Arrays.sort() 또는 Collections.sort()를 사용하면 에러가 나서 정렬 자체가 불가능하다. 

내림차순으로 정렬하는 방법

생각보다 간단하다. compare, compareTo 메소드 구현시 나오는 값을 반대로 바꿔주면 된다.

// Comparable
public int compareTo(MyClass o) {
	return -(this.value - o.value);
}
 
// Comparator
public int compare(Myclass o1, MyClass o2) {
	return -(o1.value - o2.value);
}

Comparable은 compareTo 하나밖에 구현할 수 없고, Comparator는 익명 객체로 여러개 생성할 수 있다.

 

그래서 보통 Comparable은 일반적인 설정으로 수현하고, Comparator로 오름차순, 내림차순 등 특별한 정렬을 원할 때 많이 사용된다. 참고로 String 클래스도 Comparable을 상속해서 compareTo메소드를 구현해두었다고 한다. 


이제 Comparable과 Comparator를 더 잘 사용할 수 있을 것 같다. :) 그리고 이 글에서 참고한 블로그가 자바의 기본적인 원리에 대해 정말 잘 설명해 두었다. 하나하나 다 정독해봐야겠다.


References

  • https://st-lab.tistory.com/243?category=830901