컬렉션 기반 알고리즘 Sort , <T extends Comparable<? super T>>

gustjtmd·2022년 1월 11일
0

Java

목록 보기
25/40

정렬 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)

-> 그런데 그 TComparable<T> 인터페이스를 구현한 상태이어야 한다.

마지막으로 다음과 같이 하나로 정리하자
----------------------------------------------------------------
public static <T extends Comparable<T>> void sort(List<T> list)
-> 인자로 List<T>의 인스턴스는 모두 전달 가능
->TComparable<T> 인터페이스를 구현한 상태이어야한다.

----------------------------------------------------------------
이렇게 이해하고 나면 다음과 같이 sort 메소드의 호출이 가능함을 쉽게 이해할 수 있다.
List<String> list = .....;
Collections.sort(list);	//List<T>의 인스턴스가 인자로 전달.

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 메소드를 호출할때 TCar로
결정되어 다음 형태의 메소드 호출이 진행된다
public static void sort(List<Car> list)Car는 다음 조건을 만족해야하는데 예제의 정의한 Car는 이 조건을 만족한다
(조건 CarComparable<Car>를 구현해야 한다.
--------------------------------------------------------------------
그런데 다음과 같이 Car를 상속하는 Ecar를 정의했다고 가정해보자

class Car implements Comparable<Car>{...}
class Ecar extends Car{...}	//Ecar는 Comparable<Car>를 간접 구현

그러면 EcarComparable<Car>을 구현하는(간접 구현)상태가 되었는데 이를 대상으로
다음과 같은 코드를 작성하면 컴파일이 될까??

List<Ecar> list = new ArrayList<>();
Collections.sort(list);

위와 같은 sort 메소드를 호출ㄹ하면 'T는 Ecar로 결정되어' 다음 형태의 sort메소드 호출이
진행된다.
public static void sort(List<Ecar> list)

그리고 sort 메소드가 다음과 같다고 가정하였으니 ECarComparable<Ecar>를 구현하고
있어야 sort메소드 호출에 문제가 없다.

public static <T extends Comparable<T>> void sort(List<T> list)
-> TEcar인 경우 EcarComparable<Ecar>를 구현해야함.

그러나 클래스의 구현 및 상속의 구조가 다음과 같으므로 EcarComparable<Car>는
구현하는 상태이지만 Comparable<Ecar>는 구현하지 않는 상태이다.

class Car implements Comparable<Car>{...}
class Ecar extends Car{...} // Comparable<Car>를 간접 구현한다.

때문에 위에서 보인 sort 메소드의 호출은 불가능하다 

COllections 클래스의 sort 메소드는 이러한 상황을 고려하여 다음과 같이 정의되어 있다!
--------------------------------------------------------------------
public static <T extends Comparable<? super T>>void sort(List<T> list)
-> TEcar인 경우 EcarComparable<? super Ecar>를 구현해야함.
---------------------------------------------------------------------
따라서 List<Ecar> 인스턴스를 전달하면서 sort 메소드를 호출하는 순간 TEcar가 되어
위의 메소드는 다음 형태로 호출이 되고

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{...}
profile
반갑습니다

0개의 댓글