본문 바로가기

Coding Language/Java

[Java] Linked List를 이용하여 Collections의 sort Method를 사용해보자.

[해당 포스트는 개인적으로 공부를 하고 차후에 참고용으로 하고자 작성한 것입니다.

따라서 잘못된 부분이나 부족한 부분이 있을 수 있기에 참고하시기 바랍니다.]

 

Collections는 Java에서 Java.util Package에 속해있으며, Collection Interface를 상속받은 인터페이스를 반환하는 메서드로 구성된다. 이는 기본적으로 Static으로 되어있기에 별도로 객체를 생성할 필요 없이 Collections만 호출하고 '.'을 붙이면 뒤에 사용할 수 있는 Method List가 나온다.

 

아래는 Java 공식 사이트에 언급된 설명이다.

This class consists exclusively of static methods that operate on or return collections. It contains polymorphic algorithms that operate on collections, "wrappers", which return a new collection backed by a specified collection, and a few other odds and ends.

이번에는 Collections중에 sort에 대해서만 예제로 사용하여 실습을 해보았다. 우선 공식 사이트의 Sort Method에 대한 개념을 알아보자.

 

static <T extends Comparable<? super T>> void sort(List<T> list) Sorts the specified list into ascending order, according to the natural ordering of its elements.
static <T> void sort(List<T> list, Comparator<? super T> c) Sorts the specified list according to the order induced by the specified comparator.

우선 첫 번째 Sort와 두 번째 Sort의 차이점은 Comparator를 이용하여 직접 Sort하는 방식을 지정했냐 안했냐 차이이다.

첫 번째 Method는 단순 List를 가져와 정렬해준다. 정렬할 땐 내부적으로 compareTo()라는 Method를 거쳐서 내용에 따라 정렬을 한다. 참고로 여기서 사용되는 정렬 기법은 "Quick Sort" 방식이므로, 저 Sort안에 별도로 정렬 메서드를 만들어서 처리할 필요가 없다. 가장 빠른 알고리즘을 적용하였기 때문에 Return 하는 조건만 맞으면 만사 OK다.

 

아래는 compares()가 동작되는 방식이다.

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.
  • Negative Integer: 비교대상보다 작으면 음수를 Return.
  • Zero: 비교대상과 같으면 0을 Return.
  • Positive Integer: 비교대상보다 크면 양수를 Return.

sort(List <T> list)

단순 예제를 한 번 보도록 하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
LinkedList<String> list = new LinkedList<>();
 
list.add("a");
list.add("e");
list.add("b");
list.add("d");
list.add("c");
 
Collections.sort(list);
 
Iterator<String> i = list.iterator();
        
for (String item : list) {
    System.out.println(item);
}
 
cs

 

Linked List를 생성하여 정렬되지 않은 알파벳을 넣어주고, Collections의 인자 타입이 1개인 sort Method를 호출한다.

결과는 Iterator를 통해 보면 다음과 같다.

 

1
2
3
4
5
a
b
c
d
e
cs

 

자동으로 내부에서 처리되어 알파벳순으로 정렬시켜준다. 이 방법은 단순 List 항목들만 정렬을 시켜준다. 즉, 2개 이상의 인자를 사용하는 List에서는 별도의 Comparator를 구현해서 정렬할 방법을 스스로 정해야 한다. 정렬 알고리즘을 쓰는 것이 아니라 compareTo의 Return 방식을 별도로 작성해줘야 한다는 것이다.

 

sort(List <T> list)

이번에는 인자를 2개 사용하는 Class의 값을 원하는 형태로 정렬을 해보도록 하겠다.

다음과 같이 임의의 Class를 생성하자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A {
 
    int n;
    String s;
 
    public A(int n, String s) {
        this.n = n;
        this.s = s;
    }
 
    public void output() {
        System.out.println("N : " + n);
        System.out.println("S : " + s);
    }
}
cs

 

이번 예제는 Linked List의 Type을 A라는 객체 타입으로 사용할 것이다. 이제 Main Class에 다음과 같이 LinkedList를 생성하고 LinkedList에 값을 삽입하도록 하겠다.

 

1
2
3
4
5
6
7
LinkedList<A> list = new LinkedList<>();
String[] s = { "호랑이""코끼리""토끼""고양이""강아지" };
 
for (int i = 0; i < 5; i++) {
    int value = (int) (Math.random() * 100);
    list.add(new A(value, s[i]));
}
cs

 

value에는 랜덤으로 0부터 100까지의 숫자를 넣고 s에는 String 배열의 값을 차례대로 넣도록 하겠다. 이 저 정렬을 해보자. 정렬은 기존과 다르게 2개의 인자를 사용하는 sort Method를 호출하자.

 

1
2
3
4
5
6
7
Collections.sort(list, new Comparator<A>() {
    @Override
    public int compare(A o1, A o2) {
        // TODO Auto-generated method stub
        return (o1.n > o2.n) ? +1 : -1;
    }
});
cs

 

Comparator는 Interface 이므로 익명 클래스 형태로 compare를 구현해야 한다. 값을 오름차순으로 구현하기 위해 o1이 o2(비교대상) 보다 크면 +1로 Return을 해주고 작을 경우 -1을 Return 해주기로 했다.

if ~ else 문에서 return 값을 +1과 -1을 해주는 이유는 조건의 밸런스를 맞춰주기 위함이다.
또한 if ~ else를 정석으로 사용하려면 명령문의 처리 길이도 똑같이 밸런스를 잡아주는 것이 좋다.

결과는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
N : 7
S : 토끼
N : 32
S : 호랑이
N : 34
S : 코끼리
N : 64
S : 강아지
N : 93
S : 고양이
cs

Comparator를 변형시켜보자

기존 Lambda 방식과 삼항 연산자를 이용할 줄 아는 사람이라면 코드를 보면 바꿀 수 있다는 게 눈에 익을 것이다.

방금 예제를 삼항 연산자로 바꿔보도록 하겠다.

 

1
2
3
4
5
6
7
Collections.sort(list, new Comparator<A>() {
    @Override
    public int compare(A o1, A o2) {
        // TODO Auto-generated method stub
        return (o1.n > o2.n) ? +1 : -1;
    }
});
cs

 

코드의 길이가 조금 줄어든 것을 볼 수 있다. 그럼 여기서 더 줄일 수 있는 방법이 있다. 아까 설명했듯이 비교대상보다 값이 크면 +를 작으면 -를 Return 하기만 하면 된다. 그렇다면 다음과 같이 수정할 수 있다.

 

1
2
3
4
5
6
7
Collections.sort(list, new Comparator<A>() {
    @Override
    public int compare(A o1, A o2) {
        // TODO Auto-generated method stub
        return o1.n - o2.n;
    }
});
cs

 

비교대상의 값과 바로 계산을 해서 Return 시켜주면 알아서 +인지 -인지를 반환하게 된다. 자 마지막으로 Lambda 방식으로 변경시켜보자.

 

1
2
3
Collections.sort(list, (o1, o2) -> {
        return o1.n - o2.n;
});
cs

 

new부터 Comparator <A>까지 지워주자. 그리고 Method 껍질인 public int compare (A o1, A o2)를 지우고 Scope를 같이 지워준다. 이후 받아오는 인자는 처음 괄호에서 넣어준다. 끝이다. 마지막으로 이것을 단 한 줄로만 표현을 해보자.

 

1
Collections.sort(list, (o1, o2) -> o1.n - o2.n);
cs

 

마법같이 11줄이었던 코드가 1줄로 줄어들었다. Lambda 식을 알고 있다면 이렇게 슈가 코드를 작성할 수 있다.

 

참고로 Int Type이 아닌 Float Type으로 정렬을 하게 되면 다음과 같은 방법은 오류가 난다.
Float Type으로 사용하고자 한다면 삼항 연산자까지 처리가 가능하니 참고하도록 하자.