자바

[자바] Comparable 인터페이스로 값 정렬하기

imcoding 2022. 6. 25. 15:30

 

Comparable

 

값을 비교할 때 사용하는 이 두가지는 모두 "인터페이스"이다.

각각의 추상메서드를 갖고있는 이 인터페이스들은 implements해서 사용하며, 반드시 해당 추상메서드를 implements하는 클래스 내부에서 구현해야한다. 

https://imcoding.tistory.com/10

 

인터페이스(Interface) .java

인터페이스(Interface) 인터페이스는 하나의 추상적 개념이다. 가령 자동차를 예시로 들어보자. 자동차의 사전 뜻은 다음과 같다. 자동차 (自動車) [명사] 원동기를 장치하여 그 동력으로 바퀴를 굴

imcoding.tistory.com

 

 

그렇다면 왜 해당 인터페이스를 사용할까?

primitive 타입의 데이터도 부등호만으로 비교가 가능한데 말이다.

 

만약 우리가 primitive 타입이 아닌, Map이나 클래스 객체와 같이 두 가지의 요소를 갖는 경우를 비교해야한다고 생각해보자.

class Student{
    int age;
    int score;

    Student(int age, int score) {
        this.age = age;
        this.score = score;
    }
}

위와 같이 학생 클래스를 만들고 그 안에 나이과 점수를 뜻하는 매개변수를 각각 생성했다.

여러 학생이 있을 때 각 학생들을 단순 비교한다면 나이와 점수 중 어떤 것을 기준으로 정렬될까?

이럴 때 비교를 수월하는 방법이 Comparable 인터페이스를 사용하는 것이다.

 

지금부터 Comparable 사용법을 알아보겠다.

매개변수 값을 받아서 자기 자신과 비교한다.

선언하는 방법은 아래와 같다.

class Student implements Comparable<Student> { // 1. 클래스 선언

    @Override
    public int compareTo(Student o) {	// 2. 오버라이딩
        return 0;
    }
}

먼저 클래스를 생성할 때는 상속에서는 extends였다면, 여기서는 implements를 사용한다.

그리고 Comparable 뒤에는 비교대상의 <제너릭 타입>이 쓰여야한다.

여기서는 Student 객체와 비교할 것이니 Student를 넣었다. 

생성했으면 처음에는 빨간색으로 밑줄이 그어질것이다.

인터페이스는 추상메서드를 가지고 있고, implements 한 클래스는 해당 추상메서드를 반드시 구현해야하기 때문이다.

그것이 바로 public int compareTo()라고 보면 되겠다.

 

코드를 좀 더 작성해보겠다.

class Student implements Comparable<Student> {

    int age;
    int score;

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

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}


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

        Student A = new Student(7,80);
        Student B = new Student(16, 70);

        System.out.println(A.compareTo(B));
    }
}

Student의 생성자를 생성해주었다.

이제 Student의 매개변수로 들어오는 this.age와 this.score를 가지고 compareTo를 통해 들어오는 파라미터 o와 비교 후 값이return된다고 보면 된다.

그리고 여기서 A.compareTo(B)를 해주면 단순히 A의 age가 this.age가 되고 B의 age가 o.age가 되어서 두 값의 차이를 반환해줄 것이다.

먼저 들어가는 매개면수가 this.age가 된다고 생각하면 편할것이다.

하지만 우리가 Comparable 인터페이스를 사용하는 이유는 단순히 두 값의 차이를 알기 위해서가 아니다.

그럴거였으면 다른 방식으로 얼마든지 가능하기 때문이다.

 

Comparable 인터페이스는 값 정렬에 사용할 수 있다.

아래 코드를 보자.

class Student implements Comparable<Student> {

    int age;
    int score;

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

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}

class Note{
    public static void main(String[] args) {
    
        ArrayList<Student> list = new ArrayList<>();

        list.add(new Student(17, 70));
        list.add(new Student(16, 70));
        list.add(new Student(19, 40));

        Collections.sort(list);

        for (Student student: list) {
            System.out.println(student.age + " " + student.score);
        }
    }
}

각 학생마다 age, score 순으로 데이터를 넣었다.

그리고 이를 리스트에 담고 Comparable 인터페이스를 implements한 Student 메서드를 통해서 정렬 방식을 설정해주었다.

위에서 this.age - o.age로 두 값의 차이가 반환된 것과 다르게, 여기서는 age를 기준으로 오름차순으로 정렬된다.

만약 o.age - this.age로 작성한다면? age를 기준으로 내림차순 정렬된다.

출력
16 70
17 70
19 40

이를 좀더 응용하면 아래처럼 가능하다.

class Student implements Comparable<Student> {

    int age;
    int score;

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

    @Override
    public int compareTo(Student o) {
        return this.score == o.score ? this.age - o.age : o.age - this.age;
    }
}

return값을 위 코드처럼 작성하면 어떻게 출력될 것 같은가? 이제 충분히 예상이 갈 것이다.

출력 결과를 보겠다.

출력
19 40
16 70
17 70

그렇다. score가 같을 경우에는 오름차순, 그렇지 않다면 내림차순으로 정렬된 모습이다.

여기까지 Comparable 인터페이스와 그의 추상메서드인 compareTo를 이용하여 조건하에 정렬하는 법을 알아보았다.