Comparable을 이용한 정렬
자바에서 클래스를 정렬하려면 Comparable을 구현해야 합니다. 다음 Person 클래스를 보겠습니다. 간단한 엔티티클래스입니다.
// 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가 적으면 음수가 리턴되도록 작성하면 됩니다. 동작을 확인하기 위해서 다음과 같이 테스트코드를 작성했습니다.
// 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로 정렬할 수 없습니다.
// 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 엔티티를 담은 정렬을 사용하기 위해서 다음과 같은 테스트코드를 작성하였습니다.
// 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부분을 다음처럼 작성해도 동일합니다.
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을 쓰면 다양한 정렬에 대해서 쉽게 사용할 수 있습니다.
Collections.sort(list, byAge.reverse());
Collections.sort(list, byAge.reverse().nullsFirst());
Collections.sort(list, byAge.reverse().nullsLast());
Ordering에는 정렬을 거꾸로 하는 reverse나 null의 정렬순서를 정해주는 nullsFirst, nullsLast가 있습니다. 기본적인 순서로 해주는 natural도 있습니다. 이러한 함수들을 조합해서 원하는 조합을 만들 수 있으며 좀 더 복잡한 정렬조건은 compound를 사용해서 조합할 수 있습니다.
Comments