자바 [JAVA] Comparable vs Comparator

ideal dev·2023년 1월 10일
0

Java

목록 보기
3/7

자바와 같은 객체 지향 언어를 사용하면, 객체들을 정렬해야 하는 경우가 생깁니다.

EX)

  • 좌표를 x좌표가 증가하는 순, x좌표가 같으면 y좌표가 감소하는 순으로 정렬
  • 국어점수는 증가하는 순, 수학점수는 감소하는 순으로 정렬

여러 경우 중, 예를 들어 학생들을 나이순으로 나열해야 한다고 생각했을 때 학생 클래스를 생성해보면 이렇게 됩니다.

public class Student{
	private String name;
    private int age;
    
    public Student(String name, int age){
      this.name = name;
      this.age = age;
    }
}

학생들이 이렇게 있다고 가정했을 때, 존 김초코 밥 앨리스 순으로 나열해야합니다.
이제 학생들을 어떻게 나이순으로 정렬할까요?

List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 15));
students.add(new Student("bob", 23));
students.add(new Student("kim Choco", 18));
students.add(new Student("john", 55));

제목과 같이 두 가지 방법, Comparable 과 Comparator 에 대해서 알아볼 예정입니다.

  • Comparable : 자기 자신과 매개변수 객체를 비교
  • Comparator : 두 매개변수 객체를 비교
    로 알고 들어가면 이해가 더 쉬울 것 같습니다.

객체 정렬 기준의 필요성

단순한 숫자나 문자와 같은 기본형(primitive)데이터는 자바가 일반적인 대소관계(1 보다 2가 크다, A보다 B가 크다 ) 에 따라 정렬해줍니다.

예를 들어 학생의 나이를 별도의 배열로 생성하여 Arrays.sort() 메서드를 사용하면 됩니다.

int[] ages = {15,23,18,55};
Arrays.sort(ages);
System.out.println(Arrays.toString(ages));
// 출력 : [15, 18, 23, 55]

하지만, 특정 타입의 객체는 기본형 데이터와 달라 정렬 기준이 없으면 정렬을 할 수 없으며, 정렬 기준을 정의하여 알려주어야 합니다.
그 첫번째 방법이 Comparable 입니다.

( 정렬하려 했을 시 컴파일 에러 일어남 )

Collections.sort(students); // error !!!

Comparable 인터페이스

객체의 정렬 기준을 정의하는 방법 중 하나로,
자바에서 기본적으로 제공하고 있는 Comparable 인터페이스를 구현하도록 변경하는 것 입니다.

Student 클래스에 적용하면 아래와 같이 됩니다.

class Student implements Comparable<Student>{
    private String name;
    private int age;
    
    public Student(String name, int age){
      this.name = name;
      this.age = age;
    }

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

Comparable 인터페이스의 compareTo() 메서트를 통해 인자로 넘어온 같은 타입의 다른 객체와 대소 비교 가능해졌습니다.

  • 현재 객체 < 파라미터로 넘어온 객체: 음수 리턴
  • 현재 객체 == 파라미터로 넘어온 객체: 0 리턴
  • 현재 객체 > 파라미터로 넘어온 객체: 양수 리턴
    음수 또는 0이면 객체의 자리가 그대로 유지되며, 양수인 경우에는 두 객체의 자리가 바뀝니다.

이제 Collections.sort() 메서드에는
Comparable 인터페이스를 구현한 Comparable 타입의 Student 객체의 리스트
인자로 넘어가기 때문에 더 이상 컴파일 에러가 발생하지 않습니다.
(위에 Comparable 을 구현하기 전의 빨간줄이 없어졌음을 알 수 있지용 )

정렬 후 출력

Collections.sort(students);
// [name = Aliceage = 15 ]
// [name = kim Chocoage = 18 ]
// [name = bobage = 23 ]
// [name = johnage = 55 ]

Interface Comparable 정리

정의

  • 정렬 수행 시 기본적으로 적용되는 정렬 기준이 되는 메서드를 정의하는 인터페이스
  • package: java.lang.Comparable
  • Java에서 제공되는 정렬이 가능한 클래스들은 모두 Comparable 인터페이스를 구현하고 있으며, 정렬 시에 이에 맞게 정렬이 수행
// Integer class
public final class Integer extends Number implements Comparable<Integer> { ... }
// String class
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { ... }

Ex) Integer, Double 클래스: 오름차순 정렬
Ex) String 클래스: 사전순 정렬

구현 방법

  • 정렬할 객체에 Comparable interface를 implements 후, compareTo() 메서드를 오버라이드하여 구현
  • 음수 또는 0이면 객체의 자리가 그대로 유지되며, 양수인 경우에는 두 객체의 자리 바뀜

사용 방법

  • Arrays.sort(array)
  • Collections.sort(list)

참고 Arrays.sort() 와 Collections.sort() 차이

1. Arrays.sort()

  • Primitive Array: 기본 자료형에 대한 배열
  • Object Array: 새로 정의한 클래스에 대한 배열 * Primitive Array에서는 Dual Pivot QuickSort(Quick Sort + Insertion Sort)를 사용
  • Ex) byte[], char[], double[], int[], Object[], T[] 등 * Object Array에서는 TimSort(Merge Sort + Insertion Sort)를 사용

2. Collections.sort()

  • List Collection 정렬의 경우
  • Ex) ArrayList, LinkedList, Vector 등 * 내부적으로 Arrays.sort()를 사용

Comparator 객체 사용

정렬 대상 클래스의 코드를 직접 수정할 수 없는 경우에는 어떻게 객체의 정렬 기준을 정의할 수 있을까요?
또는, 정렬하고자 하는 객체는 이미 존재하고 있는 정렬 기준과 다른 정렬 기준으로 정렬하고 싶을 때는 어떻게 해야할까요?

바로바로 Comparator 인터페이스의 구현체를
Arrays.sort()나 Collections.sort()와 같은 정렬 메서드의 추가 인자로 넘기면,
새로운 정렬 기준으로 객체를 정렬할 수 있습니다.

Comparator<Student> comparator = new Comparator<Student>(){
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
};

Collections.sort(students, comparator);

결과

System.out.println(students);
//[name = Alice, age = 15 ]
// [name = kim Choco, age = 18 ]
// [name = bob, age = 23 ]
// [name = john, age = 55 ]

위 코드는 Comparator 객체를 Collections.sort() 메서드의 두번째 인자로 넘겨 이전과 동일한 정렬 결과를 얻을 수 있습니다.
이렇게 Comparator 객체를 인자로 넘기면, 정렬 대상 객체가 Comparable 인터페이스 구현 여부와 상관없이
넘어온 Comparator 구현체의 compare() 메서드 기준으로 정렬을 수행합니다.

Interface Comparator 정리

정의

  • 정렬 가능한 클래스(Comparable 인터페이스를 구현한 클래스)들의 기본 정렬 기준과 다르게 정렬 하고 싶을 때 사용하는 인터페이스
  • package: java.util.Comparator
  • 주로 익명 클래스로 사용
  • 기본적인 정렬 방법인 오름차순 정렬을 내림차순으로 정렬할 때 많이 사용

구현 방법

  • Comparator interface를 implements 후 compare() 메서드를 오버라이드한 myComparator class를 작성한다.
  • 음수 또는 0이면 객체의 자리가 그대로 유지, 양수인 경우에는 두 객체의 자리가 변경

사용 방법

  • Arrays.sort(array, myComparator)
  • Collections.sort(list, myComparator)
  • Arrays.sort(), Collections.sort() 메서드는 두 번째 인자로 Comparator interface를 받을 수 있음

참고

두 번째 인자로 Comparator interface를 받는 경우

  • 우선 순위 큐(PriorityQueue) 생성자의 두 번째 인자로 Comparator interface를 받을 수 있음
  • 지정된 Comparator의 정렬 방법에 따라 우선 순위 할당
PriorityQueue(int initialCapacity, Comparator<? super E> comparator)

람다 함수로 대체

Comparator 객체는 메서드가 하나 뿐인 함수형 인터페이스를 구현하기 때문에 람다 함수로 대체가 가능

Collections.sort(students, (a, b) -> a.getAge() - b.getAge());
System.out.println(students);
// [name = Alice, age = 15 ]
// [name = kim Choco, age = 18 ]
// [name = bob, age = 23 ]
// [name = john, age = 55 ]

참고
https://www.daleseo.com/java-comparable-comparator/
https://st-lab.tistory.com/243
https://gmlwjd9405.github.io/2018/09/06/java-comparable-and-comparator.html

0개의 댓글