본문 바로가기
✏️Java 공부/기타 등등

[Java 공부/기타] 객체 정렬하기 (Comparable & Comparator)

by 코코의 주인 2022. 8. 1.

 백준 문제를 풀다 보면 정렬이 필요한 문제가 종종 나온다. 대부분의 경우 Arrays.sort()나 Collections.sort()를 호출하여 해결할 수 있지만 정렬에 추가적인 조건이 붙었거나, 사용자가 정의한 객체를 정렬하게 될 경우 추가적인 작업이 필요하다.

 이를 위해 필요한 Comparable과 Comparator에 대해 알아보고, 활용하는 법을 배워보자.


Comparable

 Comparable 인터페이스는 컬렉션을 정렬하는데 필요한 메서드를 정의하고 있다.

int[] arr = {1, 3, 5, 7, 2, 4, 6};
Arrays.sort(arr);	//arr이 {1, 2, 3, 4, 5, 6, 7}로 정렬

 위 예제는 int형 배열을 정렬하는 코드다. Arrays.sort()를 호출하면 컴퓨터가 알아서 배열을 정렬해준다. 이것이 가능한 이유는 Integer클래스에 Comparable이 구현되어 있기 때문이다.

 Comparable은 객체를 어떤 기준에 맞춰 정렬할 것인지를 해당 객체의 클래스에 구현해 두었다. 때문에 정렬이 가능한 클래스에는 모두 Comparable이 구현되어 있다.

 

사용자가 정의한 클래스를 정렬할 경우

import java.util.*;

class Animal {
    private String name;    //이름
    private String species; //종
    private int date;   //등록일

    Animal(String n, String s, int d) {
        this.name = n;
        this.species = s;
        this.date = d;
    }
}

class sort {
    public static void main(String[] args) {
        List<Animal> animals = new ArrayList<>();
        animals.add(new Animal("coco", "cat", 20200314));
        animals.add(new Animal("momo", "dog", 20180617));
        animals.add(new Animal("bori", "dog", 20220228));
        animals.add(new Animal("tori", "cat", 20151030));
        animals.add(new Animal("hodu", "dog", 20170621));

        Collections.sort(animals);
    }
}

 

 

 

 Collections.sort()를 호출하였으나 컴파일 에러가 발생하면서 코드가 실행이 되지 않는다. Animal 클래스에 Comparable이 구현되어있지 않기 때문에 어떤 기준으로 이 객체를 정렬해야 하는지 알지 못해서 정렬이 이루어지지 못하는 것이다. 이를 해결하기 위해서는 Animal 클래스에 Compalable를 구현해줘야 한다.

 

Comparable 구현하기

public interface Comparable {
	public int compareTo(Object o);
}

 Comparable의 실제 소스는 위와 같다. Comparable 인터페이스를 구현하기 위해서는 compareTo(Object o) 메서드에 대한 이해가 필요하다.

 

 compareTo(Object o) 메서드는 해당 객체(this)와 전달받은 객체(o)의 값을 비교하여 아래와 같은 값을 반환한다.

 

  • 비교하는 두 객체가 같으면 : 0 리턴
  • 해당 객체의 값 < 전달 받은 객체의 값 : 음수 리턴
  • 해당 객체의 값 > 전달 받은 객체의 값  : 양수 리턴

이에 맞게 compareTo()를 구현해주어야 한다.

 

예제

class Animal implements Comparable{
	
    /*생략*/
    
    public int compareTo(Animal a) {
        if(this.date > a.date) {
            return 1;
        }
        else if(this.date < a.date) {
            return -1;
        }
        else {
            return 0;
        }
    }
}

Animal 클래스의 등록일에 따라 오름차순으로 객체들이 정렬되도록 compareTo() 메서드를 구현했다. Comparable 인터페이스의 compareTo() 메서드를 구현한 뒤 Collections.sort()를 호출하면 정상적으로 정렬이 이루어지는 것을 확인할 수 있다.

 

정렬 결과

정렬 전
이름:coco     종:cat    등록일:20200314
이름:momo  종:dog   등록일:20180617
이름:bori      종:dog   등록일:20220228
이름:tori       종:cat    등록일:20151030
이름:hodu    종:dog   등록일:20170621

정렬 후
이름:tori       종:cat     등록일:20151030
이름:hodu    종:dog    등록일:20170621
이름:momo  종:dog    등록일:20180617
이름:coco     종:cat     등록일:20200314
이름:bori      종:dog    등록일:20220228

 

 

 


Comparator

 Comporable을 구현한 클래스들은 기본적으로 오름차순에 의해 객체를 정렬하도록 되어있다. 하지만 내림차순으로 정렬을 하고 싶다거나 다른 기준에 의해서 정렬되도록 하고 싶을 때 Comparator를 구현하여 정렬 기준을 바꿀 수 있다.

 Comparator를 구현하기 위해서는 compare(Object o1, Object o2)를 오버라이딩하여 구현하여야 한다. compare() 역시 객체를 비교해서 음수, 0, 양수 중 하나를 반환하도록 구현해야 한다.

 

  • 비교하는 두 객체가 같으면 : 0 리턴
  • o1의 값 < o2의 값 : 음수 리턴
  • o1의 값 > o2의 값  : 양수 리턴

예제

이번에는 Animal 클래스에 새로운 정렬 기준을 정의해보겠다.

 

 1) 종을 사전순으로 오름차순 정렬

 2) 종이 같으면 등록일 순으로 오름차순 정렬

 

 이렇게 정렬 기준이 복잡한 경우 Comparator를 활용하여 해결할 수 있다.

        Collections.sort(animals, new Comparator<Animal>() {
            @Override
            public int compare(Animal o1, Animal o2) {
                if(o1.species.equals(o2.species)) {	//종이 같으면
                    return o1.date - o2.date;	//등록일 비교
                }
                else {	//종이 다르면
                    return o1.species.compareTo(o2.species);	//종 사전순 비교
                }
            }
        });

 종이 같을 경우 등록일을 비교한다. o1.date - o2.date를 반환 값으로 설정하면

 

  • o1.date = o2.date : 0 반환
  •  o1.date > o2.date : 양수 반환
  • o1.date < o2.date : 음수 반환

의 효과가 있기 때문에 같은 종 내에서 오름차순 정렬을 할 수 있다.

 

종이 다른 경우 사전순으로 비교한다. 이때, compareTo() 메서드를 활용하면

 

  • o1.species의 순서가 o2.species보다 앞일 때 : 음수 반환
  • o1.species의 순서가 o2.species보다 뒤일 때 : 양수 반환

을 자동으로 해주기 때문에 종을 사전순으로 오름차순 정렬할 수 있게 해 준다.

 

정렬 결과

정렬 전
이름:coco        종:cat      등록일:20200314
이름:momo     종:dog     등록일:20180617
이름:bori         종:dog     등록일:20220228
이름:tori          종:cat      등록일:20151030
이름:hodu       종:dog     등록일:20170621

정렬 후
이름:tori         종:cat      등록일:20151030
이름:coco       종:cat      등록일:20200314
이름:hodu      종:dog     등록일:20170621
이름:momo    종:dog     등록일:20180617
이름:bori        종:dog     등록일:20220228

 

관련 문제

 Comparator를 활용하여 해결할 수 있는 문제를 골라봤다. 이 글을 통해 잘 이해하였다면 한번 풀어보면서 연습하는 것을 추천한다.

 

https://www.acmicpc.net/problem/1181

 

1181번: 단어 정렬

첫째 줄에 단어의 개수 N이 주어진다. (1 ≤ N ≤ 20,000) 둘째 줄부터 N개의 줄에 걸쳐 알파벳 소문자로 이루어진 단어가 한 줄에 하나씩 주어진다. 주어지는 문자열의 길이는 50을 넘지 않는다.

www.acmicpc.net

풀이 :

2022.08.01 - [🧑‍💻코딩 테스트/백준 (BOJ)] - [코딩 테스트/백준 알고리즘] 1181번 : 단어 정렬 (Java 풀이)

 

 

https://www.acmicpc.net/problem/11650

 

11650번: 좌표 정렬하기

첫째 줄에 점의 개수 N (1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개의 줄에는 i번점의 위치 xi와 yi가 주어진다. (-100,000 ≤ xi, yi ≤ 100,000) 좌표는 항상 정수이고, 위치가 같은 두 점은 없다.

www.acmicpc.net

풀이 :

2022.08.01 - [🧑‍💻코딩 테스트/백준 (BOJ)] - [코딩 테스트/백준 알고리즘] 11650번 : 좌표 정렬하기 (Java 풀이)


총평

 이렇게 정리하고 보니 좀 더 이해가 된 거 같아서 기분이 좋다.

댓글