학습목표
정렬 알고리즘을 풀다가, String비교연산의 특징과 Comparable, Comparator의 사용법에 익숙치 않아 풀지못하고, 타 블로그를 참고해서 푼 문제가 있다. 분명 처음 접근을 Comparator를 써야겠다. 생각했었으나, 사용법과 String 비교연산에 대한 개념?이 없어 시동하다가 도중에 포기했다. 이번에 해당 내용을 학습하면서 명확히 기억해보도록 하자!!
- String 대소비교
- Comparable
- Comparator
- 실습 코드
String 대소비교
compareTo();
기준값.compareTo(비교값);의 형태로 사용되는 메소드다. 기준 값과 비교 값의 대소를 비교해 리턴해준다. String에 선언되어있는 compareTo의 코드를 살펴 보자.
public int compareTo(String paramString) {
int i = value.length;
int j = value.length;
int k = Math.min(i, j);
char[] arrayOfChar1 = value;
char[] arrayOfChar2 = value;
int m = 0;
while (m < k) {
int n = arrayOfChar1[m];
int i1 = arrayOfChar2[m];
if (n != i1) {
return n - i1;
}
m++;
}
return i - j;
}
int i, j가 모두 같은것을 가르키는것 같지만, i는 기분값을, j는 비교값(paramString)을 의미한다.
왜 그런지는 명확히 모르겠다. 추후에 또 알아보자.
비교대상이 되는 두 문자열중 작은 문자열의 길이를 기준으로 while 문을 수행한다. 만약, 모든 문자열의 값이 같다면, 두 문자열의 차이값을 리턴하고, 그게 아니라면, 문자열의 값이 다른 인덱스에서 (기준값 - 비교값) 의 값을 리턴한다. 문자열의 기본 비교는 사전순서이고,
(문자열이 숫자로만 이루어져있다면 큰수가 더 뒤에 있다.)
즉, a - z 는 음수가 나오며, a가 z보다 사전순서상 앞서있다는 것이다. (compareTo()메소드만 제대로 알았어도.. ㅠ)
Comparable
public interface Comparable
This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's compareTo method is referred to as its natural comparison method.
Lists (and arrays) of objects that implement this interface can be sorted automatically by Collections.sort (and Arrays.sort). Objects that implement this interface can be used as keys in a sorted map or as elements in a sorted set, without the need to specify a comparator.
Java8 API문서의 글이다. Comparable은 이 인터페이스를 구현한 클래스의 natural ordering을 부과한다고 쓰여있다.
자연순서란, 끼리끼리의 순서를 나타낸다. 즉, int형끼리의 순서, String끼리의 순서를 말한다.
List나 Array는 Collections.sort()와 Arrays.sort()로 구현된다.
그럼 Comparable 인터페이스가 선언한 메소드를 살펴보자.
int compareTo(T o)
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
기준 값이 비교 값(T o)와 비교해 작으면 음수를, 같으면 0, 크면 양수를 리턴하도록 되어있다. 위의 String의 compareTo(String paramString)메소드를 보면 더 확실히 이해할수 있다.
즉, comparable 인터페이스를 구현한 클래스는 클래스의 객체들간의 대소비교가 가능해지는 것이다. 그렇기 때문에, String또한 Comparable을 구현, compareTo()메소드로 대소비교가 가능한것이다. 그럼, Comparator와는 무엇이 다른걸까??
Comparator
Functional Interface:
This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.
@FunctionalInterface
public interface Comparator
A comparison function, which imposes a total ordering on some collection of objects. Comparators can be passed to a sort method (such as Collections.sort or Arrays.sort) to allow precise control over the sort order. Comparators can also be used to control the order of certain data structures (such as sorted sets or sorted maps), or to provide an ordering for collections of objects that don't have a natural ordering.
먼저 Comparator는 Functional Interface이므로 람다 표현식에서 사용할 수 있다. 물론 Comparable도.... 쓸수 있긴하다... 이건 크게 중요한것 같지 않고, 중요한건 natural ordering이 없는 객체들에게 순서를 부과할수 있다는 것이다. 즉, 내맘대로의 커스텀한 기준하에 sorting을 할수있다는 뜻이다.
이 특성을 이용, 정렬 알고리즘을 풀때 도움이 많이 될것으로 생각된다. (또한 natural ordering이 없는 객체라면 대게 개발자가 직접 만든 클래스들이 많이 그럴것으로 예상된다.)
그럼 Comparator인터페이스가 선언한 메소드를 알아보자.
int compare(T o1, T o2)
Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
Parameters:
o1 - the first object to be compared.
o2 - the second object to be compared.
compareTo()와 비슷한 흐름이다. o1이 첫번째, o2는 두번째 순서이다.
실습코드
- 사용법을 몰라 갈곳 잃은 나의 코드(였.던.것),,,
import java.util.Comparator;
public class Sort2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] numbers = {6, 10, 2};
Sort2 srt2 = new Sort2();
System.out.println(srt2.solution(numbers));
String a = "90";
String b = "909";
System.out.println(a.compareTo(b));
}
public String solution(int[] numbers) {
String answer = "";
return answer;
}
private class arrClass implements Comparator<String>{
@Override
public int compare(String var1, String var2) {
// TODO Auto-generated method stub
return var1.split("")[0].compareTo(var2.split("")[0]);
}
}
}
/* 결과
-1
*/
??... 뭐하는걸까... comparator로 어떻게 해보겠다고 compare를 구현해두고, 정작 compareTo를 호출하다니?.. 대충 Comparator로 sorting 기준을 바꿀수있다는 것만 알고 접근했던것 같다...
- Comparable
위의 코드를 이용해 Comparable을 사용해 보자!
import java.util.Comparator;
public class Sort2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] numbers = {6, 10, 2};
String[] words = new String[numbers.length];
Sort2 srt2 = new Sort2();
for(int i=0; i< numbers.length; i++) {
words[i] = String.valueOf(numbers[i]);
// Integer.toString()과 String.valueOf() 중 어떤게 더 효율적일까?..
}
Arrays.sort(words);
for(String tmpWord : words) {
System.out.print(tmpWord+" ");
}
}
/*결과
10 2 6
*/
String끼리의 natura ordering을 통한 결과, 10 2 6 의 순서가 나왔다. 숫자의 대소비교와는 다르다! String은 각 인덱스의 char값이 다르다면, 거기서 바로 판별한다. 즉, 10의 경우 첫번째 char가 1이므로, 가장 먼저 나온것이다.
Integer.toString() VS String.valueOf()
public static String valueOf(int paramInt) {
return Integer.toString(paramInt);
}
ㅎㅎ.. 똑같다.
- Comparator
import java.util.Arrays;
import java.util.Comparator;
public class Sort2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] numbers = {6, 10, 2};
Sort2 srt2 = new Sort2();
System.out.println(srt2.solution(numbers));
}
public String solution(int[] numbers) {
String[] words = new String[numbers.length];
String answer = "";
for(int i=0; i<numbers.length; i++) {
words[i] = String.valueOf(numbers[i]);
}
Arrays.sort(words, new Comparator<String>() {
public int compare(String var1, String var2) {
return (var2+var1).compareTo(var1+var2);
}
});
for(String word : words) {
answer += word;
}
return answer;
}
/*결과
6210
*/
Comparator를 사용해서 '가장 큰 수'의 알고리즘을 구현했다. 물론 예외처리 해야할게 더 있지만, 기본적인 알고리즘은 이렇게 구현한다.
왜 var2+var1을 var1+var2와 비교할까?
우선 String 연산은 숫자와 달리 연산되는 순서에 따라 값이 바뀜을 다시 체크하자!
먼저, String의 compareTo는 문자열이 모두 같다면, 문자열 길이의 차를, 다르다면, 문자열이 달라진 인덱스에서 String에 적용된 사전순서상대로 대소를 비교, 리턴한다.
만약 단순히 var2.compareTo(var1); 을 실행할 경우 [3,30,34] 과 같은 경우에 대해서 3을 30과 34비교시 모두 문자열 1개차이로 -1을 출력, 대소비교가 제대로 되지 않는 문제가 있다.
그래서 var2+var1과 같이 문자열의 길이를 동일하게 맞춰주는 작업이 필요하다.
그렇다면, 왜 (var2+var1)이 기준값이 되는걸까? (var1+var2)가 기준이 되면 안되는 걸까?
답은간단하다.
var1+var2가 기준이 되면 오름차순으로 정렬된다. 즉, 앞자리에 9에 가까운 수부터 출력되는것이 내가 찾는 답이지만, 오름차순으로 정렬되면 1부터가까운 수부터 출력 될것이다. (가장 작은 수를 만들것이다)
compare의 동작방식
실습코드를 보면서 디버깅을 했는데, 이상한 결과가 나왔다.
??.. 왜 var1이 10이고, var2가 6일까??... 분명 API에서는 var1이 첫번째로 비교되는 객체, var2가 두번째로 비교되는 객체라고 적혀있었다. 쓰여진대로라면, 분명 var1 =6, var2=10이 나와야 정상이다. 연산을 해봐도 var1 =6, var2 =10이 나와야 6210이 나올수있다. 흠... 결과는 제대로 나오는데... 나중에 디버깅을 좀더 자세히 해봐야겠다.
Feedback
둘의 차이는, Comparable은 대개 이미 제공되는 클래스들간의 natural ordering을 제공, 개발자가 이용할수 있고, Comparator는 Comparable이 없는 객체들이거나, 혹은 natural ordering이외 특수한 기준으로 sorting해야할때 사용하는 interface라고 생각된다.
그리고... 문자열이 숫자로만 이루어져있을땐 큰수가 앞에 나온다.. 라고 쓴건 무슨 생각에서 였을까;; 집중안하고 있다는 증거... 인가..
알게된 점
- String의 대소비교
- Comparable을 구현한 객체들의 비교정렬 방법
- Comparator를 이용한 natural ordering외의 정렬 방법
- '가장 큰 수' 알고리즘 풀이
알아야 할 점
- compare의 동작방식
References
[Java] Comparable와 Comparator의 차이와 사용법 - Heee's Development Blog
'프로그래밍 > Java' 카테고리의 다른 글
Dynamic Proxy직접 구현해보기-1 (0) | 2021.08.10 |
---|---|
Comparator의 동작방식 (0) | 2021.07.27 |
정적 언어와 동적언어 그리고 덕 타이핑 (0) | 2021.07.22 |
서블릿 (1) | 2021.07.12 |
Spring VS Spring Boot 의 배포차이 (0) | 2021.07.03 |