배열안에 담겨있는 값(VO)들을 특정 기준으로 비교하여 순서를 정렬하기 위한 방법을 알아보자.

 

1. Car 객체

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package comparator;
 
public class Car {
    
    private String name;
    private int price;
    
    public Car (String name, int price) {
        this.name = name;
        this.price = price;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getPrice() {
        return price;
    }
    public void setPrice(int price) {
        this.price = price;
    }
}
cs

 

2. Car 객체 정렬 및 실행을 위한 Main 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package comparator;
 
import java.util.Arrays;
 
public class Main {
 
    public static void main(String[] args) {
        
        Car c1 = new Car("K5"2000);
        Car c2 = new Car("A6"8000);
        Car c3 = new Car("BMW3"4000);
        
        Car[] cars = {c1, c2, c3};
        
        Arrays.sort(cars);
 
        for(Car tmp : cars) {
            System.out.println(tmp.getName()+" ");
        }        
    }
}
 
cs

'정렬기준이 없는데?' 라고 생각 들 수 있겠지만 일단 실행시켜보자.

 

역시나 에러가 발생한다.

comparator.Car 가 java.lang.Comparable 로 캐스팅 될 수 없다는 캐스팅 에러.

Arrays.sort 내에서 comparator, comparable 을 사용하다 에러가 발생했다.

Exception in thread "main" java.lang.ClassCastException: comparator.Car cannot be cast to java.lang.Comparable
at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:290)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:157)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at comparator.Main.main(Main.java:19)

 

그럼 이제 Arrays.sort 를 탐구해보자.

 

※ Arrays.sort 

아래는 java.util.Arrays 의 정적메소드 sort.

정확한 파악은 힘들지만 대충 살펴보았을 때 2번째 인자인 Comparator c 값의 유무에 따라 사용되는 메소드가 분기처리 되는 듯 하다.

1) 은 인자가 한개인경우 호출되는 메소드,

2) 는 인자가 두개인 경우 호출되는 메소드

 

1) Arrays.sort(배열) 일 경우 사용되는 method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low,
                                  int high,
                                  int off) {
        int length = high - low;
 
        // Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i++)
                for (int j=i; j>low &&
                         ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
                    swap(dest, j, j-1);
            return;
        }
 
        // Recursively sort halves of dest into src
        int destLow  = low;
        int destHigh = high;
        low  += off;
        high += off;
        int mid = (low + high) >>> 1;
        mergeSort(dest, src, low, mid, -off);
        mergeSort(dest, src, mid, high, -off);
 
        // If list is already sorted, just copy from src to dest.  This is an
        // optimization that results in faster sorts for nearly ordered lists.
        if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }
 
        // Merge sorted halves (now in src) into dest
        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }
cs

내부적으로 객체를 Comparable 로 형변환을 한 후 compareTo 메소드를 호출하여 값을 비교(정렬) 하고 있다.

첫번째 인자로 넘겨준 Car 객체는 Comparable(인터페이스) 를 구현하고 있지 않으므로 Comparable 객체로 형변환시 cast exception이 발생한 것이다.

 

그럼 Car 객체가 Comparable 인터페이스를 구현하도록 Car Class 를 아래와 같이 수정해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package comparator;
 
public class Car implements Comparable<Car> {
    
    private String name;
    private int price;
    
    public Car (String name, int price) {
        this.name = name;
        this.price = price;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getPrice() {
        return price;
    }
    public void setPrice(int price) {
        this.price = price;
    }
    
    @Override
    public int compareTo(Car c) {
        
        int comPrice = c.getPrice();
        
        return this.price-comPrice;
    }
}
cs

 

다시 한 번 2번의 Main 클래스를 실행해보자

실행 결과는 아래와 같이 성공이다.

※ Car 객체의 compareTo 메소드의 return 부분(30 line : this.price-comPrice)

을 comPrice-this.price 와 같이 반대로 바꾸면 정렬 기준이 바뀐다(오름차순/내림차순).

 

위와 같이 VO 클래스를 Comparable interface 를 구현하도록 수정 후 compareTo 를 overriding 하면 원하는 방식으로 정렬을 시킬 수 있다.

VO 클래스를 이와 같이 매번 구현하여 사용하긴 번거로운데, 다른 방법은 없을까?

 

Arrays.sort 를 다시 한 번 들여다 보자.

2) Arrays.sort(배열, Comparator ?) 일 경우 사용되는 method가 아래와 같이 구현되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low, int high, int off,
                                  Comparator c) {
        int length = high - low;
 
        // Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i++)
                for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--)
                    swap(dest, j, j-1);
            return;
        }
 
        // Recursively sort halves of dest into src
        int destLow  = low;
        int destHigh = high;
        low  += off;
        high += off;
        int mid = (low + high) >>> 1;
        mergeSort(dest, src, low, mid, -off, c);
        mergeSort(dest, src, mid, high, -off, c);
 
        // If list is already sorted, just copy from src to dest.  This is an
        // optimization that results in faster sorts for nearly ordered lists.
        if (c.compare(src[mid-1], src[mid]) <= 0) {
           System.arraycopy(src, low, dest, destLow, length);
           return;
        }
 
        // Merge sorted halves (now in src) into dest
        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }
cs

내부적으로 인자값으로 넘어온 Comparator의 compare 메소드를 호출하여 값을 비교(정렬) 하고 있다.

 

Comparable 을 구현한 Car 객체는 원복 시킨 후, Main 클래스를 아래와 같이 수정해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package comparator;
 
import java.util.Arrays;
import java.util.Comparator;
 
public class Main {
 
    public static void main(String[] args) {
        
        Car c1 = new Car("K5"2000);
        Car c2 = new Car("A6"8000);
        Car c3 = new Car("BMW3"4000);
        
        Car[] cars = {c1, c2, c3};
        
        System.out.println("sort start!");
        
        Arrays.sort(cars, new Comparator<Car>(){
            @Override
            public int compare(Car c1, Car c2) {
                //return c1.getPrice() - c2.getPrice();
                return c2.getPrice() - c1.getPrice();
            }
        });
        
        for(Car tmp : cars) {
            System.out.println(tmp.getName()+" ");
        }
        
    }
 
}
 
cs

Arrays.sort 의 두번째 인자값으로 Comparator 를 넘겨주었다. (익명클래스로 구현)

 

실행시 결과는 아래와 같다.

 

사족 : Comparable, Comparator를 통해 바라본 인터페이스에 대해..

Arrays.sort 메소드는 내부적으로 파라미터 값으로 받은 객체의 compareTo 메소드 및 Comparable interface 의 compare 메소드를 사용하고 있다.

어떤 자료형의 파라미터 값이 넘어올지 Arrays 의 sort 메소드는 알 수 없지만 파라미터로 넘겨받은 객체 내에 compareTo 가 존재하리라 가정하고 소스가 짜여져 있다.

이처럼 인터페이스는 구현을 강제하며(Car 클래스의 compareTo 메소드) 소스간 결합도(Arrays.sort 와 Car클래스)를 약하게 한다. 또한 내공이 필요 하겠으나, 개발자들은 이를 활용하여 다양한 디자인 패턴의 개발을 할 수 있다..

 

 

 

반응형

+ Recent posts