정렬 Sort
- 들어가기 앞서 <? extends T>, <? super T>에 대해서 복습해보자.
<? extends T> -- 상한 제한 T이거나 T를 상속하는 클래스들만 가능하다.
(T이거나 T의 자식 클래스들) (꺼내는 것 Get만 가능)
<? super T> -- 하한 제한 T이거나 T가 상속하는 클래스들만 가능하다.
(T이거나 T의 부모 클래스들) (넣는것 Set만 가능)
List<E>를 구현한 컬렉션 클래스들은 저장된 인스턴스 정렬된 상태로 유지하지 않는다.
대신에 정렬을 해야 한다면 다음 메소드를 사용할수 있다.
public static <T extends Compareble<T>> void sort(List<T> list)
위의 메소드는 Collections 클래스에 정의되어 있는 제네릭 메소드이다.
처음 보면 복잡해보이니 메소드를 줄여놓고 시작하자.
public static <T> void sort(List<T> list)
-> 메소드가 호출 시점에 T가 정해지므로 List<T>의 인스턴스는 모두 전달 가능.
그 후에 다음 내용을 추가해보자.
----------------------------------------------------------------
public static <T extends Comparable<T>> void sort(List<T> list)
-> 그런데 그 T는 Comparable<T> 인터페이스를 구현한 상태이어야 한다.
마지막으로 다음과 같이 하나로 정리하자
----------------------------------------------------------------
public static <T extends Comparable<T>> void sort(List<T> list)
-> 인자로 List<T>의 인스턴스는 모두 전달 가능
-> 단 T는 Comparable<T> 인터페이스를 구현한 상태이어야한다.
----------------------------------------------------------------
이렇게 이해하고 나면 다음과 같이 sort 메소드의 호출이 가능함을 쉽게 이해할 수 있다.
List<String> list = .....;
Collections.sort(list);
STring은 다음과 같이 Comparable<String>을 구현한다. 따라서 위에서 보이듯이
List<String>인스턴스는 sort 메소드의 인자로 전달이 될 수 있다.
public final class String extends Object implements Comparable<String>
예제-------------------------------------------------------------------
List<String> list = Arrays.asList("Toy", "Box", "Robot", "Weapon");
list = new ArrayList<>(list);
for(Iterator<String> itr = list.iterator(); itr.hasNext();)
System.out.print(itr.next()+ '\t');
System.out.println();
Collections.sort(list);
for(Iterator<String> itr = list.iterator(); itr.hasNext();)
System.out.print(itr.next()+ '\t');
System.out.println();
----------------------------------------------------------------------
Toy Box Robot Weapon
Box Robot Toy Weapon
<T extends Comparable<? super T>>
사실 Collections클래스의 sort의 메소드는
public static <T extends Comparable<? super T>>void sort(List<T> list)
이다.
-----------------------------------------------------------------------
일단 sort메소드가
public static <T extends Comparable<T>> void sort(List<T> list)
와 같다고 생각하고 아래 예제를 분석해보자
------------------------------------------------------------------------
class Car implements Comparable<Car>{
private int disp;
public Car(int d){disp = d;}
@Override
public String toString(){
return "cc: "+disp;
}
@Override
public int compareTo(Car o){
return disp - o.disp;
}
}
public class CarSortCollections {
public static void main(String[] args) {
List<Car> list = new ArrayList<>();
list.add(new Car(1200));
list.add(new Car(3000));
list.add(new Car(1800));
Collections.sort(list);
for (Iterator<Car> itr = list.iterator(); itr.hasNext();)
System.out.println(itr.next().toString()+'\t');
}
}
-------------------------------------------------------------------
cc: 1200
cc: 1800
cc: 3000
여전히 sort메소드가 다음과 같이 정의 되어 있다고 가정하고
public static <T extends Comparable<T>> void sort(List<T> list)
---------------------------------------------------------------------
그러면 List<Car> 인스턴스를 인자로 전달하며 sort 메소드를 호출할때 T는 Car로
결정되어 다음 형태의 메소드 호출이 진행된다
public static void sort(List<Car> list)
단 Car는 다음 조건을 만족해야하는데 예제의 정의한 Car는 이 조건을 만족한다
(조건 Car는 Comparable<Car>를 구현해야 한다.
--------------------------------------------------------------------
그런데 다음과 같이 Car를 상속하는 Ecar를 정의했다고 가정해보자
class Car implements Comparable<Car>{...}
class Ecar extends Car{...}
그러면 Ecar는 Comparable<Car>을 구현하는(간접 구현)상태가 되었는데 이를 대상으로
다음과 같은 코드를 작성하면 컴파일이 될까??
List<Ecar> list = new ArrayList<>();
Collections.sort(list);
위와 같은 sort 메소드를 호출ㄹ하면 'T는 Ecar로 결정되어' 다음 형태의 sort메소드 호출이
진행된다.
public static void sort(List<Ecar> list)
그리고 sort 메소드가 다음과 같다고 가정하였으니 ECar는 Comparable<Ecar>를 구현하고
있어야 sort메소드 호출에 문제가 없다.
public static <T extends Comparable<T>> void sort(List<T> list)
-> T가 Ecar인 경우 Ecar는 Comparable<Ecar>를 구현해야함.
그러나 클래스의 구현 및 상속의 구조가 다음과 같으므로 Ecar는 Comparable<Car>는
구현하는 상태이지만 Comparable<Ecar>는 구현하지 않는 상태이다.
class Car implements Comparable<Car>{...}
class Ecar extends Car{...}
때문에 위에서 보인 sort 메소드의 호출은 불가능하다
COllections 클래스의 sort 메소드는 이러한 상황을 고려하여 다음과 같이 정의되어 있다!
--------------------------------------------------------------------
public static <T extends Comparable<? super T>>void sort(List<T> list)
-> T가 Ecar인 경우 Ecar는 Comparable<? super Ecar>를 구현해야함.
---------------------------------------------------------------------
따라서 List<Ecar> 인스턴스를 전달하면서 sort 메소드를 호출하는 순간 T는 Ecar가 되어
위의 메소드는 다음 형태로 호출이 되고
public static void sort(List<Ecar> list)
메소드 선언에서 T가 구현해야할 인터페이스를 Comparable<? super T>로 명시 했으므로
Ecar 클래스는 다음 인터페이스중 하나만 구현해도 위의 sort 메소드는 호출은 성공한다.
Comparable<Object>, Comparable<Car>, Comparable>Ecar>
예제를 통해서 설명한 내용을 확인하고 정리하자
---------------------------------------------------------------------
class Car implements Comparable<Car>{
protected int disp;
public Car(int d){disp = d;}
@Override
public String toString(){
return "cc: disp";
}
@Override
public int compareTo(Car o){
return disp - o.disp;
}
}
class Ecar extends Car{
private int battery;
public Ecar(int d, int b){
super(d);
battery = b;
}
@Override
public String toString() {
return "cc : " + disp + ", ba: " + battery;
}
}
public class EcarSortCollections {
public static void main(String[] args) {
List<Ecar> list = new ArrayList<>();
list.add(new Ecar(1200,99));
list.add(new Ecar(3000,55));
list.add(new Ecar(1800,87));
Collections.sort(list);
for(Iterator<Ecar> itr = list.iterator(); itr.hasNext();)
System.out.println(itr.next().toString() +'\t');
}
}
-------------------------------------------------------------------------
cc : 1200, ba: 99
cc : 1800, ba: 87
cc : 3000, ba: 55
----------------------------------------------------------------------
이제 이후로 다음과 같은 유형의 메소드 선언을 본다면
public static <T extends Comparable<? super T>> void sort(List<T> list)
그리고 위 메소드에 대한 다음 질문의 답을 타인에게 혹은 본인 스스로에게 한다면
Comparable<T>가 아닌 Comparable<? super T>인 이유는?
언급한 다음 클래스 구조를 바탕으로 설명을 하고 이해를 하자.
class Car implements Comparable<Car>{...}
class ECar extends Car{...}