[Info]Tags categorized posts and contents patterns..

[AJAX] Ajax Code E xamples.. [Book] About the book.. [CSS] CSS Code E xamples.. [DB] Sql Code E xamples.. [DEV] All development stor...

2016년 4월 5일 화요일

[JAVA]Guava로 Ordering으로 컬렉션 정렬하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

얼마전에 Guava에 대해서 Guava를 써야하는 5가지 이유라는 포스팅 했었는데 컬렉션 위주로 조금씩 써보고 있습니다.

Comparable을 이용한 정렬
자바에서 클래스를 정렬하려면 Comparable을 구현해야 합니다. 다음 Person 클래스를 보겠습니다. 간단한 엔티티클래스입니다.


Java
 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
// Person.java
package kr.sideeffect;

public class Person implements Comparable<Person> {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    public int compareTo(Person o) {
        return this.getAge() - o.getAge();
    }
}

자바의 Collections의 sort함수를 사용하려면 담고 있는 클래스가 위처럼 Comparable인터페이스를 구현해서 compareTo함수를 구현해 놓아야 합니다. compareTo함수를 이용해서 정렬의 조건을 정해줄 수 있는데 여기서는 나이순으로 정렬하기 위해서 age필드를 비교했습니다. 두 값이 같으면 0, this가 크면 양수, this가 적으면 음수가 리턴되도록 작성하면 됩니다. 동작을 확인하기 위해서 다음과 같이 테스트코드를 작성했습니다.


Java
 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
// PersonTest.java
package kr.sideeffect;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;

public class PersonTest {
    @Test
    public void 정렬테스트() throws Exception {
        // given
        Person p1 = new Person("Outsider", 32);
        Person p2 = new Person("nephilim", 40);
        Person p3 = new Person("Anarcher", 35);
        Person p4 = new Person("fupfin", 43);
        Person p5 = new Person("Arawn", 33);
        
        List<Person> list = new ArrayList<Person>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);
        list.add(p5);
        
        // when
        Collections.sort(list);
    
        // then
        assertEquals(list.get(0).getName(), p1.getName());
        assertEquals(list.get(1).getName(), p5.getName());
        assertEquals(list.get(2).getName(), p3.getName());
        assertEquals(list.get(3).getName(), p2.getName());
        assertEquals(list.get(4).getName(), p4.getName());
    }
}


이 테스트 코드는 성공합니다. compareTo를 작성하였기 때문에 나이순으로 잘 정렬이 됩니다.(위 예제의 나이는 실제인물과는 상관없이 예제를 위한 것임을 밝힙니다.) 하지만 정렬을 해야할 때마다 Comparable을 구현해주어야 하는건 꽤나 귀찮은 일이고(당연한 일인지 모르겠지만 전 무척 귀찮더군요.) 정렬을 여러가지 해야할 경우에는 더욱 피곤해 집니다.

Guava의 Ordering
Guava에서는 Ordering이라는 클래스로 정렬을 제공할 수 있습니다. Ordering을 테스트하기 위해서 Geek이라는 새로운 엔티티를 만들었고 Comparable을 구현하지 않았으므로 Collections.sort로 정렬할 수 없습니다.


Java
 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
// Geek.java
package kr.sideeffect;

public class Geek {
    private String name;
    private int age;
    
    public Geek(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}


이 Geek 엔티티를 담은 정렬을 사용하기 위해서 다음과 같은 테스트코드를 작성하였습니다.

Java
 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
41
42
43
44
// GeekTest.java
package kr.sideeffect;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Ints;

public class GeekTest {
    @Test
    public void 정렬테스트() throws Exception {
        // given
        Geek g1 = new Geek("nephlim", 40);
        Geek g2 = new Geek("Anarcher", 35);
        Geek g3 = new Geek("fupfin", 43);
        Geek g4 = new Geek("Arawn", 33);
        
        List<Geek> list = new ArrayList<Geek>();
        list.add(g1);
        list.add(g2);
        list.add(g3);
        list.add(g4);
    
        // when
        Ordering<Geek> byAge = new Ordering<Geek>() {
            @Override
            public int compare(Geek left, Geek right) {
                return Ints.compare(left.getAge(), right.getAge());
            }
        };
        
        Collections.sort(list, byAge);
    
        // then
        assertEquals(list.get(0).getName(), g4.getName());
        assertEquals(list.get(1).getName(), g2.getName());
        assertEquals(list.get(2).getName(), g1.getName());
        assertEquals(list.get(3).getName(), g3.getName());
    }
}

when에 작성한 부분이 Ordering을 사용한 부분입니다. new Ordering으로 새로운 클래스를 만들면서 Ordering의 compare를 오버라이드라이드해서 정렬의 기준을 작성해 주면 됩니다. 예제에서는 Guava가 프리미티브타입에 대한 유틸리티성 클래스인 Ints를 사용해서 compare로 두 값을 비교했습니다. 사실 이는 Comparator인터페이스를 이용해서 when부분을 다음처럼 작성해도 동일합니다.

Java
1
2
3
4
5
6
7
Comparator<Geek> byAge = new     <Geek>() {
    public int compare(Geek left, Geek right) {
        return ((Integer)left.getAge()).compareTo((Integer)right.getAge());
    }
};
        
Collections.sort(list, byAge);

하지만 Ordering을 쓰면 다양한 정렬에 대해서 쉽게 사용할 수 있습니다.

Java
1
2
3
Collections.sort(list, byAge.reverse());
Collections.sort(list, byAge.reverse().nullsFirst());
Collections.sort(list, byAge.reverse().nullsLast());

Ordering에는 정렬을 거꾸로 하는 reverse나 null의 정렬순서를 정해주는 nullsFirst, nullsLast가 있습니다. 기본적인 순서로 해주는 natural도 있습니다. 이러한 함수들을 조합해서 원하는 조합을 만들 수 있으며 좀 더 복잡한 정렬조건은 compound를 사용해서 조합할 수 있습니다.

댓글 없음:

댓글 쓰기