1. comparable과 comparator
Java 에서 object를 정렬 하기 위해서는 comparable interface 가 기본적으로 필요하다.
@Test
public void sortObjects() {
final String[] strings = {"z", "yy", "abc"};
final String[] expected = {"abc", "yy", "z"};
Arrays.sort(strings);
assertArrayEquals(expected, strings);
}
String class는 comparable 인터페이스가 implements된 class 이므로 순서대로 정렬된다.
comparable이 implements되지 않은 object를 sorting 하려고 하면 컴파일러가 comparable interface가 구현되어 있을거라고 예상하기 때문에 exception이 발생한다.
class Obj {
private int orderNo;
public Obj(int i) {
this.orderNo = i;
}
public int getOrderNo() {
return this.orderNo;
}
}
@Test
public void sortNotComparable() {
final List<Obj> objects = new ArrayList<>();
for (int i = 0; i < 10; i++) {
objects.add(new obj(i));
}
// java.lang.ClassCastException: Sorting$Obj cannot be cast to java.lang.Comparable
Arrays.sort(objects.toArray());
}
Comparable interface를 implements해서 compareTo 메소드를 override하면 sorting을 할 수 있다.
class Obj implements Comparable<Obj>{
private int orderNo;
...
@Override
public int compareTo(Obj o) {
return this.orderNo - o.orderNo;
}
}
하지만 클래스 외부에서 compare 방법을 변경할 수 없기 때문에 보통 comparable을 설명 할때 natural order sorting(예를 들어 id순서나 날짜 순서 같은 자연스러운 sorting)을 구현할때 사용하라고 하는 경우를 볼 수 있다. 우리가 알고 있는 디자인 패턴에 맞는 방법은, 다양한 sorting방법을 변경하면서 사용하는 것인데, 그래서 comparator 가 필요하다.
class CustumOrder implements Comparator {
@Override
public int compare(Obj o1, Obj o2) {
return o2.getOrderNo() - o1.getOrderNo();
}
}
class Obj {
private int orderNo;
public Obj(int i) {
this.orderNo = i;
}
public int getOrderNo() {
return this.orderNo;
}
@Override
public boolean equals(Object other) {
Obj self = (Obj) other;
return this.orderNo == self.getOrderNo();
}
}
class CustumOrder implements Comparator {
@Override
public int compare(Obj o1, Obj o2) {
return o2.getOrderNo() - o1.getOrderNo();
}
}
@Test
public void sortNotComparable() {
final List<Obj> objects = new ArrayList<>();
for (int i = 0; i < 5; i++) {
objects.add(new Obj(i));
}
final List<Obj> reversedExpectedObjects = Arrays.asList(new Obj(4), new Obj(3), new Obj(2), new Obj(1), new Obj(0));
final List<Obj> expectedObjects = Arrays.asList(new Obj(0), new Obj(1), new Obj(2), new Obj(3), new Obj(4));
// 내림차순
Collections.sort(objects, new CustumOrder());
assertEquals(reversedExpectedObjects, objects);
// 오름차순
// Comparator가 functional interface이기 때문에 lambda expression을 사용할 수 있다.
Collections.sort(objects, new Comparator<Obj>() {
@Override
public int compare(Obj o1, Obj o2) {
return o1.getOrderNo() - o2.getOrderNo();
}
});
assertEquals(expectedObjects, objects);
}
2. TreeMap
자바에서 기본 자료구조로 TreeMap을 제공한다. HashMap과 달리 이진트리 자료구조이기 때문에 key를 가지고 comparable과 comparator interface를 사용해서 sorting한다. (그렇기 때문에 hashCode 메소드는 사용하지 않는다.)
@Test
public void treeMapTraversal() {
final Map<Integer, String> counts = new TreeMap<>();
counts.put(4, "four");
counts.put(1, "one");
counts.put(3, "three");
counts.put(2, "two");
final Iterator<Integer> keys = counts.keySet().iterator();
assertEquals("one", counts.get(keys.next()));
assertEquals("two", counts.get(keys.next()));
assertEquals("three", counts.get(keys.next()));
assertEquals("four", counts.get(keys.next()));
}
@Test
public void treeMapTraversalWithComparator() {
final Map<Integer, String> counts = new TreeMap<>(new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
counts.put(4, "four");
counts.put(1, "one");
counts.put(3, "three");
counts.put(2, "two");
final Iterator<Integer> keys = counts.keySet().iterator();
assertEquals("four", counts.get(keys.next()));
assertEquals("three", counts.get(keys.next()));
assertEquals("two", counts.get(keys.next()));
assertEquals("one", counts.get(keys.next()));
}
'programing' 카테고리의 다른 글
리액트 상태관리를 하는 가장 쉬운 방법 Plo (0) | 2020.03.12 |
---|---|
gitignore 패턴 (0) | 2017.07.20 |
가상 DOM 이란? (3) | 2016.06.21 |
클로저 시작하기 (0) | 2016.06.09 |