Comparable vs Comparator in Java

2021. 6. 25. 14:26JAVA

자바는 클래스의 데이터 멤버를 사용하여 객체를 정렬할 수 있는 2가지 인터페이스를 제공한다.

  1. Comparable
  2. Comparator

 

Comparable 인터페이스의 사용

Comparable 객체는 자신을 다른 개체와 비교할 수 있다. Comparable 인터페이스를 사용하고자 하는 클래스는 객체들을 비교하기 위해서 java.lang.Comparable 인터페이스를 상속(implements)받아 구현해야 한다.

예를 들어 Movie 클래스가 존재할때 데이터 멤버로는 등급(rating), 이름(name), 개봉년도(year)와 같은 멤버를 가질수 있다. 개봉년도를 기반으로 영화들의 정렬을 원한다고 가정할 때 Movie 클래스는 Comparable 인터페이스를 상속받을 수 있다. 그리고 Movie 클래스는 Comparable 인터페이스의 compareTo() 메서드를 오버라이드(Override)하여 구현해야 한다.

Comparable 인터페이스의 사용, 소스코드

// Comparable 인터페이스를 상속한 Movie 클래스
public class Movie implements Comparable<Movie>{
	private String name;
	private double rating;
	private int year;
	
	// year를 사용하여 Movie 객체들을 정렬한다.
	@Override
	public int compareTo(Movie m) {
		return this.year - m.year;
	}

	public Movie(String name, double rating, int year) {
		this.name = name;
		this.rating = rating;
		this.year = year;
	}

	public double getRating() {
		return rating;
	}

	public String getName() {
		return name;
	}

	public int getYear() {
		return year;
	}

	@Override
	public String toString() {
		return name + " " + rating + " " + year;
	}
}
// Comparable 사용을 시연하는 자바 프로그램
// Driver calss
public class Main {

	public static void main(String[] args) {
		List<Movie> list = new ArrayList<>();
		list.add(new Movie("Force Awakens",8.3,2015));
		list.add(new Movie("Star Wars",8.7, 1977));
		list.add(new Movie("Empire Strikes Back",8.8,1980));
		list.add(new Movie("Return of the Jedi",8.4,1983));
		
		Collections.sort(list);
		System.out.println("Movies after sorting : ");
		for(Movie movie : list)
		{
			System.out.println(movie);
		}
	}

}
Movies after sorting : 
Star Wars 8.7 1977
Empire Strikes Back 8.8 1980
Return of the Jedi 8.4 1983
Force Awakens 8.3 2015

이제는 영화를 등급(rating)과 이름(name)으로 분류하고자 한다. 그러나 비교 가능한 컬렉션 요소를 생성할 때 우리는 compareTo() 메서드를 구현하기 위해서는 오직 한번 밖에 없다. 이것에 대한 문제는 Comparator를 사용하여 해결한다.

 

Comparator 인터페이스 사용

 Comparable과는 달리 Comparator는 우리가 비교하는 요소 타입이 외부에 존재한다. Comparator는 분리된 클래스이다. 다른 데이터 멤버들에 의해서 비교하기 위해서 Comparator를 상속받은 여러개의 분리된 클래스들을 생성한다. Collections 클래스는 sort() 메서드가 존재하고 Comparator를 사용한다. sort() 메서드는 객체들을 정렬하기 위해서 compare() 메서드와 연관이 있다. 등급을 기준으로 영화를 비교하기 위해서 3가지가 필요하다.

  1. Comparator를 상속(implements)받은 클래스 생성
  2. Comparator 클래스의 인스턴스를 생성
  3. Comparator를 구현하는 클래스의 목록과 인스턴스를 모두 제공하면서 오버라이드 sort() 메서드를 호출한다.

Comparator 인터페이스 사용, 소스코드

// Comparable 인터페이스를 상속한 Movie 클래스
public class Movie implements Comparable<Movie>{
	private String name;
	private double rating;
	private int year;
	
	// year를 사용하여 Movie 객체들을 정렬한다.
	@Override
	public int compareTo(Movie m) {
		return this.year - m.year;
	}

	public Movie(String name, double rating, int year) {
		this.name = name;
		this.rating = rating;
		this.year = year;
	}

	public double getRating() {
		return rating;
	}

	public String getName() {
		return name;
	}

	public int getYear() {
		return year;
	}

	@Override
	public String toString() {
		return name + " " + rating + " " + year;
	}
}
//등급을 기준으로 Movie 객체들을 비교한다.
public class RatingCompare implements Comparator<Movie>{

	@Override
	public int compare(Movie m1, Movie m2) {
		// m1의 등급이 m2의 등끕보다 작으면 음수 반환
		if(m1.getRating()<m2.getRating())
		{
			return -1;
		}
		// m1의 등급이 m2의 등급보다 크면 양수 반환
		else if(m1.getRating()>m2.getRating())
		{
			return 1;
		}
		// m1의 등급과 m2의 등급과 같으면 0 반환
		else
		{
			return 0;
		}
		
	}

}
// 이름을 기준으로 Movie 객체들을 비교한다.
public class NameCompare implements Comparator<Movie>{

	@Override
	public int compare(Movie m1, Movie m2) {
		return m1.getName().compareTo(m2.getName());
	}

}
// Comparable 사용을 시연하는 자바 프로그램
// Driver calss
public class Main {

	public static void main(String[] args) {
		List<Movie> list = new ArrayList<>();
		list.add(new Movie("Force Awakens",8.3,2015));
		list.add(new Movie("Star Wars",8.7, 1977));
		list.add(new Movie("Empire Strikes Back",8.8,1980));
		list.add(new Movie("Return of the Jedi",8.4,1983));
		
		// 등급을 기준으로 정렬
		// (1) ratingCompare의 객체를 생성한다.
		// (2) Collections.sort 호출
		// (3) 정렬된 리스트 출력
		System.out.println("Sorted by rating");
        RatingCompare ratingCompare = new RatingCompare();
        Collections.sort(list, ratingCompare);
        for (Movie movie: list)
            System.out.println(movie);
        
        // 이름을 기준으로 정렬
        System.out.println("\nSorted by name");
        NameCompare nameCompare = new NameCompare();
        Collections.sort(list, nameCompare);
        for (Movie movie: list)
            System.out.println(movie);
        
        // 년도를 기준으로 정렬 (Comparable 사용)
        System.out.println("\nSorted by year");
        Collections.sort(list);
        for (Movie movie: list)
            System.out.println(movie);
	}

}
Sorted by rating
Force Awakens 8.3 2015
Return of the Jedi 8.4 1983
Star Wars 8.7 1977
Empire Strikes Back 8.8 1980

Sorted by name
Empire Strikes Back 8.8 1980
Force Awakens 8.3 2015
Return of the Jedi 8.4 1983
Star Wars 8.7 1977

Sorted by year
Star Wars 8.7 1977
Empire Strikes Back 8.8 1980
Return of the Jedi 8.4 1983
Force Awakens 8.3 2015
  • Comparable은 자연스러운 순서가 있는 객체에 대한 것으로 객체 자체가 스스로 순서화를 알고 있어야 한다는 것을 의미한다. 예를 든다면 학생들의 학번같은 경우이다. 반면 Comparator 인터페이스 정렬은 분리된 클래스를 통해서 수행된다.
  • Comparable 인터페이스는 this를 통한 멤버와 매개변수로 받은 변수와 비교를 수행한다. 그리고 Comparator 인터페이스는 서로 다른 객체 2개를 매개변수로 받아 비교를 수행한다.
  • 만약 어떤 클래스가 Comparable 인터페이스를 상속받아 구현한다면 List 이거나 Array인 해당 객체의 컬렉셔은 Collections.sort() 이거나 Arrays.sort() 메서드를 사용하여 자동적으로 정렬이 가능하다. 그리고 객체들은 CompareTo 메서드에 정의된 순서를 기반으로 정렬될 것이다.
  • Comparable과 Comparator의 차이점은 Comparable은 오직 하나의 비교(compareTo 메서드)만 사용 가능하고 반면에 우리는 한개 이상의 커스텀한 Comparator를 작성할 수 있다. comparable 예제에서와 같이 하나의 속성에 의해서 정렬이 가능하였다. 다시말해서 comparator에서 등급, 이름, 연도와 같은 다른 속성들을 사용할 수 있다.

Summarize

개체 정렬이 자연스러운 순서에 기반해야 하는 경우 Comparable 인터페이스를 사용하고 다른 개체의 특성에 대해 정렬해야 하는 경우에는 Comparator 인터페이스를 사용한다.

 

References

GeeksForGeeks, Comparable vs Comparator in Java, https://www.geeksforgeeks.org/comparable-vs-comparator-in-java/