반응형

Comparator와 Comparable

  • 객체 정렬에 필요한 메서드(정렬기준 제공)를 정의하 인터페이스

Comparator와 Comparable은 객체 정렬에 필요한 메서드(정렬기준 제공)를 정의한 인터페이스다.

이름이 비슷한데,
Comparable은 기본정렬기준을 구현하는데 사용한다. 즉 default정렬기준을 제공할 때사용하고, 그외에는
Comparator을 사용하는데, 기본 정렬기준 외에 다른 기준으로 정렬하고자할 때 사용한다.


Comparator는 compare(Object o1, Object o2)라는 메서드를 가지고 있는데,
o1과 o2값을 비교해서 어느쪽이 더 큰지를 정수값으로 반환한다.
만약 반환값이 0이면 o1과 o2가 같은 것이다.
그리고, 반환값이 양수면 왼쪽이 큰것이고,
반환값이 음수면 오른쪽이 큰것이다.
cmpareto(Object o) 는 주어진 객체(o)를 자신(this)와 비교한다

sort() 즉, 정렬은
1. 두 대상을 비교해서
2. 자리바꿈을 하는 것이다. 이것을 반복하는 것이다 .

그래서 compare()와 compareTo()는 두 메서드 모두 두 대상을 비교한다.
둘다 정렬에 사용되고, 정렬기준을 제공한다. ex)오름차순, 내림차순
정렬기준이 다양한데, 우리가 정렬기준을 줄 때, 사용하는 것이 Comparable과 Comparator 인터페이스다.

  • compare()와 compareTo()는 두 객체의 비교결과를 반환하도록 작성

위 코드는 Integer클래스에 있는 내용을 그대로 가져온 것인데,
Comparable인터페이스는 무엇을 구현해야 하냐면, compareTo() 메서드를 구현해야 한다.
추상메서드의 몸통을 만들어 줘야하는데,
this.value()와 매개변수로 제공된 anotrherInteger객체의 value를 각각 v1, v2에 담은다음,
이 두값을 비교를 하도록 작성했다.
오른쪽이 크면, -1, 둘이 같으면 0, 오른쪽 값이 작으면 1을 반환한다.
오름차순으로 정렬을 하려면,
왼쪽값이 더 클 때, 자리바꿈을 해야한다.

내림차순일때는 언제 자리를 바꿀까?
오른쪽 값이 더 클 때 자리바꿈을 해야한다.
즉, 오름차순일때와 내림차순일때는 자리를 바꾸는 상황이 다르다.
중요한 것은 0, -1, +1 이 셋중에 하나의 값을 반환하도록 되어있다 라는 것이다.
return에서는 3항연산자를 2번 사용했다.
처음에 v1 < v2 를 비교하고, 참이면 ?의 오른쪽에 있는 -1 로간다.
거짓이면 두번째 연산인 v1==v2로 가서 참이면 ?의 오른쪽에 있는 0으로 가고,
거짓이면 1을 반환한다.


Comparator와 Comparable - 예제

"cat", "Dog", "lion", "tiger"이 있는데, 이것들을 정렬하면, 결과가
[Dog, cat, lion, tiger]로 나온다. Dog가 첫번째인 이유는, D가 대문자이기 때문이다.
그리고 strArr을 CASE_INSENSITIVE_ORDER로 정렬한다.
strArr은 정렬 대상이고, CASE_INSENSITIVE_ORDER은 정렬기준이다.

CASE_INSENSITIVE_ORDER은 String클래스에 있는 Static이라서
사용할 때는 String.CASE_INSENSITIVE_ORDER이렇게 사용한다.
CASE_INSENSITIVE_ORDER는 Comparator이다. 미리 String클래스에 해당 Comparator를 만들어 놓은 것이다.
CaseInsensitiveComparator는 String클래스의 내부클래스이다.Comparator인터페이스를 구현한 것이다.
정렬할 때 필요한 것은
1. 정렬 대상
2. 정렬 기준
이다. 기준이 없으면 정렬할 수 없다.
그런데, "Arrays.sort(strArr)에는 정렬 기준이 없는 것 아닌가요?" 라는 의문이 들 수 있다.
정렬할 때는 대상과 기준이 필요하다.
즉 sort가 작업을 하려면 대상과 기준이 제공되어야 한다.

그런데, 첫번째 메서드는 정렬대상만있다.
그러면 어떻게 정렬될까?
이럴 때는, 객체 배열에 저장된 백체가 구현한 Comparable이라는 정렬 기준을 가지고 있는 경우에만
첫번째 메서드처럼 작성할 수 있다.
String클래스가 Comparable을 구현했다는 말인데,
Class String implements Comparable
이때, Comparable은 기본 정렬기준이고,
이 안에는 compareTo() 메서드르 가지고 있다.
즉, String클래스자체가 기본정렬기준을 가지고 있다.
그래서 sort()에서 정렬 대상만 제공해줘도 정렬이 가능한 것이다.
그런데, 문자열의 기본정렬기준은 사전순서(ABC순서)이다.
그래서 첫번째 결과를 보면, 기본정렬기준에 의해서 정렬이 된 것이다.
그런데 만약에 이정렬기준 말고 다르게 정렬하고 싶다면, ex 대소문자 구분안하고 정렬하기
Arrays.sort(strArr, StringCASE_INSENSITIVE_ORDER) 이렇게 따로 정렬기준을 줘야한다.
그래서 두번째 결과는 car이 첫번째인채로로 정렬되었다. 왜냐하면 정렬기준이 대소문자를 구별하지 않기 때문이다.
만약에 역순으로 정렬하고 싶으면, (내림차순)
Arrays.sort(strArr, new Descending()); 이렇게 또다른 정렬기준을 주었다.
정렬기준은 밑에서 Comparator인터페이스를 구현해서 만들었다.
즉, compare 메서드를 완성해준다는 이야기다.
내림차순으로 정렬하고 싶으므로 기본정렬 기준에 * -1을 해주는 방식으로 구현했다.
그러면, 결과값이 양수→음수, 음수→양수 , 0은 그대로 0 이 되어 기본정렬을 이용하여 내림차순을 구현할 수 있다.
혹은 c1과 c2의 자리를 바꿔줘도 된다.


Integer와 Comparable

Integer클래스의 실제 코드인데,
Comparable을 구현했다. Comparable인터페이스가 하는 일은, 기본(default) 정렬기준을 제공하는 것이다.
compareTo()메서드는 기본정렬 기준을 제공하는 메서드다.
Integer라든가 String, Float등 비교가 가능한 클래스들은 다들 Comparable인터페이스를 구현하고 있다.
즉, 자체적으로 기본 정렬기준을 가지고 있다.
그래서 구현부를 보면, this.value로 자기 자신의 값과, 매개변수로 받은 anotherInteger.value를 비교한다.
비교는 3항연산자를 2번사용해서 비교한다.
왼쪽이 크면 -1이고, 오른쪽이 크면 1, 같으면 0을 반환한다.
이것을 쉽게 이야기하면, thisVal - anotherVal를 하면 된다.
뺄셈의 결과가 0이면 둘이 같다는 뜻이고,
뺄셈의 결과가 음수가 나오면 오른쪽이 크다는 이야기고,
뺄셈의 결과가 양수가 나오면 왼쪽이 크다는 이야기다.
그런데 왜 복잡하게 3항연산자를 썻을까?
3항연산자를 사용해서 작성하는게 성능이 더 빠르기 때문에 3항연산자를 사용했다.


"정렬 기준을 제공하는 건 알겠는데, 실질적으로 정렬하는 코드는 어디있나요?"
정렬은 두 대상을 비교, 자리바꿈을 반복하는 것이다.
정렬의 방법은 굉장히 다양하다.
- 버블 정렬
- 삽입 정렬
- 선택 정렬
- 퀵 정렬
- 쉴 정렬
- 병합 정렬
등 굉장히 다양한데,
이 많은 정렬 방법들이, 두 대상을 비교해서 자리바꿈을 반복하는 개념은 동일하다.
대신 전략이 다르다.
어떤것을 선택해서 선택할지같은 전략이 다른것이지,
두개를 비교해서 자리바꿈을 반복한다는 것은 동일하다.
그리고, 이미 잘 만들어져 있다.
그런데, 정렬 기준은 확장할 수 없다.
매번 달라질 수 있기 때문이다.
정렬 방법은 "불변"이지만,
정렬 기준은 "가변"이다.
그래서 불변하는 부분은 놔두고,
가변하는 부분인 "정렬 기준" 만 제공해주면 되는 것이다.

이것은 버블정렬 코드이다.
integer배열을 정렬하는 코드이다.
이코드에서는 integer인 경우에는 오름차순으로 정렬을 하는 것이다.

1. 두개 비교
2. 자리 바꿈
이 코드는 반복문을 이용해서 만들었는데,
우리가 정렬하고 싶은 대상이나 방법이 바뀌어도,
이 전체적인 부분은 불변이다.
다만, 비교를 어떻게 할지만 바뀐다.
if(intArr[j] > intArr[j+1]) 이부분만 가변이다.
그러니까 정렬 로직은 바뀌지 않는다.
대신에 우리는, 정렬 기준만 제공하면 된다.
ex) 나이순으로 할것인지, 성적순으로 할 것인지, 오름차순, 내림차순으로 할 것인지 등

정렬 대상과 정렬 기준을 주면,
Comparator c 를 이용해서 정렬하게 된다.
해당 기준으로 비교를 해서 자리바꿈을 한다.
네모 박스의 코드는 손댈 필요가 없다.
비교기준은 Comparator로 밖에서 받도록 되어있다.
대신에 우리가 할 일은, 밖에서 정렬 기준만 주면 된다.
안에 있는 정렬 방법은, 이미 극도록 효율적으로 만들어져 있다.
즉 방법은 최적화 되어있고,
우리가 할 일은 정렬 할 때 사용하는 비교기준을 제공하기만 하면 된다.

반응형

'JAVA' 카테고리의 다른 글

HashSet (2)  (0) 2022.05.02
HashSet (1)  (0) 2022.05.01
Arrays  (0) 2022.04.30
Iterator, Enumeration, Map과 Iterator  (0) 2022.04.29
Stack, Queue 활용  (0) 2022.04.29