Skills/Java

Java - Comparable & Comparator

aoaa 2022. 11. 1. 20:42

 우테코 프리코스 1주차 미션 중, 다중정렬조건을 적용하던 중 Comparator에 대한 이해가 부족하여 정리하고자 작성한 글입니다.


1. Comparable

 java.lang.Comparable의 인터페이스인 Comparable는 compareTo라는 메서드가 선언되어 있습니다. 이는 인터페이스이기 때문에 Comparable을 사용하려면 compareTo 메서드를 override해줘야 합니다. 하지만! Java 8부터는 인터페이스에서도 일반 메서드를 구현할 수 있도록 변경되었습니다. 

 Comparable은 정렬 수행시 기본적으로 적용되는 정렬 기준이 되는 메서드를 정의해 놓은 인터페이스로 Java에서 제공되는 정렬이 가능한 클래스(Integer, Double, Long 등 내림차순, String 클래스의 사전순으로 정렬)가 구현되어 있습니다.

 이 Comparable의 용도는 자기 자신과 객체를 비교할 수 있도록 합니다. compareTo() 메서드의 패러미터를 보면 하나임을 확인할 수 있는데 이 패러미터가 객체를 비교할 기준을 정의해줍니다. 이 CompareTo는 문자열 비교와 숫자 비교 두 방식이 존재합니다.

 

1.1 숫자형 비교

public class CompareTonum{
    public static void main(String[] args){

        Integer x = 1;
        Integer y = 2;
        Double z = 3.0;

        System.out.println( x.compareTo(y) );  // -1
        System.out.println( x.compareTo(1) );  //  0
        System.out.println( y.compareTo(1) );  //  1
        System.out.println( z.compareTo(2.7) );  //  -1
    }
}

 기준 값과 비교 대상이 동일하다면 0, 기준값이 비교대상보다 작다면 -1, 기준 값이 비교대상보다 크다면 1을 반환합니다. 

1.2 문자열 비교

public class CompareTostring{
    public static void main(String[] args){

        String str = "abcd";

        // 1) 비교대상에 문자열
        System.out.println( str.compareTo("abcd") );  // 0
        System.out.println( str.compareTo("ab") );  //  2
        System.out.println( str.compareTo("a") );  //  3
        System.out.println( str.compareTo("c") );  //  -2       

        // 2) 비교대상과 다른 문자열
        System.out.println( str.compareTo("zefd") );  //  -25
        System.out.println( str.compareTo("zd") );  //  -25
        System.out.println( str.compareTo("ABCD") );  //  32
    }

}

 문자열 비교같은 경우는 기준(str)에 비교대상이 포함되어있는 경우 서로의 문자열 길이의 차이값을 return하게 됩니다.

str "abcd"를 "abcd"와 비교했을 때, 숫자와 마찬가지로 0을 return해줍니다. 이어지는 값도 동일하게

"abcd" (4) - "ab"(2) = 2, "abcd" - "a"(1) = 3, "abcd" - ""(0) = 4이 됩니다. 

 이 때, 같은 위치의 문자만 비교하기 때문에, 첫 번째 문자부터 순서대로 비교해서 다르다면 ASCII값을 기준으로 비교를 하게됩니다. 

str.compareTo("c")같은 경우 ASCII값이 a=97, c=99이기 때문에 순서에 따라 -2를 return하게 됩니다.

 비교대상과 다른 문자열인 경우도 마찬가지로 비교 불가능한 지점의 문자열 ASCII값을 기준으로 합니다. 

str.compareTo("zefd"), str.compareTo("zd")의 경우 a=97, z=122이기 때문에 차이값인 -25가 반환됩니다. 


2. Comparator

 java.util.Comparator의 인터페이스 Comparator는 정렬 가능한 클래스들의 기본 정렬 기준(기본적으로 오름차순)과는 다른 방식으로 정렬할 때 사용합니다. 

int compare​(T o1, T o2)
Parameters:
o1 - 비교될 첫 번째 오브젝트
o2 - 비교될 두 번째 오브젝트
Returns:
o1이 o2 오브젝트보다 작으면 -1(음수), 같으면 0, 크면 1(양수)를 반환
Throws:
NullPointerException - 지정한 개체 o가 null인 경우
ClassCastException - o가 비교할 다른 class 타입일 경우

 int compare 메서드를 설명하자면, 정렬이 진행될 때 자리바꿈(=정렬) 여부를 결정하는 값을 넘겨주는 역할을 합니다.
만약 return값이 0이나 음수이면 자리바꿈을 하지 않고, 양수이면 자리바꿈을 수행합니다.
만약 오름차순이 아니라 내림차순으로 정렬하고 싶다면 매개변수의 순서를 바꿔주면 됩니다. 두 수의 비교 결과에 따른 작동 방식이 음수라면 교환하지 않고, 양수라면 두 원소의 위치를 교환하게 됩니다. 예시를 들어보겠습니다.

import java.lang.Comparable;
import java.util.Arrays; 

class Student implements Comparable<Student> {
	String name; 
	int id;
	double score; 
	public Student(String name, int id, double score){
		this.name = name;
		this.id = id;
		this.score = score;
	}
	public String toString(){ //출력용 toString오버라이드
		return "이름: "+name+", 학번: "+id+", 학점: "+score;
	}
	/* 기본 정렬 기준: 학번 오름차순 */
	public int compareTo(Student anotherStudent) {
		return Integer.compare(id, anotherStudent.id);
	}
}

public class Main{
	public static void main(String[] args) {
		Student student[] = new Student[5];
		//순서대로 "이름", 학번, 학점
		student[0] = new Student("Dave", 20120001, 4.2);
		student[1] = new Student("Amie", 20150001, 4.5);
		student[2] = new Student("Emma", 20110001, 3.5);
		student[3] = new Student("Brad", 20130001, 2.8);
		student[4] = new Student("Cara", 20140001, 4.2);
		Arrays.sort(student); 
		for(int i=0;i<5;i++) 
			System.out.println(student[i]);
	}
}

 

 학생들 중에 성적우수자 2명을 선정하여 장학금을 지급한다고 가정해봅시다.

이 때, 성적이 같은 학생이 여러 명이라면 학번이 빠른 순서대로 정하려고 합니다. 이를 찾기 위해서는 "성적이 높은 순으로 정렬한 뒤, 앞에서 2명을 선택"해야하는데 어떻게 하면 좋을까요? 기본 정렬 기준(오름차순)과 다른 새로운 정렬 기준을 세워야 하는데, 이 때 이용되는 것이 바로 Comparator입니다. 주로 익명 클래스로 사용되며, 이를 Arrays.sort()내부에 정렬 기준으로 구현하면 됩니다. 

 

Arrays.sort(student, new Comparator<Student>(){
	@Override
	public int compare(Student s1, Student s2) {
		double s1Score = s1.score;
		double s2Score = s2.score;
		return Double.compare(s2Score, s1Score);//학점 내림차순
	}
});
Arrays.sort(student, new Comparator<Student>(){
	@Override
	public int compare(Student s1, Student s2) {
		double s1Score = s1.score;
		double s2Score = s2.score;
		if(s1Score == s2Score){ //학점이 같으면
			return Double.compare(s1.id, s2.id); //학번 오름차순
		}
		return Double.compare(s2Score, s1Score);//학점 내림차순
	}
});

 2번째 조건인 '학점이 같으면 학번 오름차순' 정렬 조건을 추가한 모습입니다. id를 비교하여 학점이 같다면 compare로 학번을 비교하여 다중 조건정렬을 할 수 있게 됩니다. 그렇다면 Amie, Dave, Cara 순으로 정렬된 것을 확인할 수 있습니다.

 

 참고로 이 익명 클래스를 람다식(lambda)으로 comparator를 생성할 수도 있습니다.

Collections.sort(strings, (s1, s2) -> s1.length() - s2.length());

 

 

 

 

 

 

 

 

 

참조

더보기