Java

정렬을 돕는 Comparable, Comparator

자바에는 정렬을 도와주는 인터페이스인 Comparable, Comparator 가 있습니다.Collection 을 통해 정렬을 간편히 수행할 수 있는 데, Comparable 이나 Comparator 를 구현하는 구현체이거나 람다를 이용하여 정렬방법을 구현해야 합니다.

 

primitive 타입 배열과 같은 것들은 자연스러운 순서로 정렬이 됩니다.(Arrays.sort 는 DualPivotQuicksort 라는 알고리즘을 사용합니다.)

Comparable

Comparable 은 일반적으로 생각할 수 있는 정렬을 정의할 때 구현하는 인터페이스입니다. (물론, 꼭 그럴 이유는 없습니다만 JavaDoc 의 주석으로 되어있으므로, 일반적인 용례를 생각하면 그에 따르는 것이 혼란을 줄일 수 있습니다.)

Comparable 을 구현하면 compareTo 를 구현해야 합니다.

public class Compare implements Comparable<Compare> {
  private int number;

  public Compare(int number) {
    this.number = number;
  }

  @Override
  public int compareTo(Compare o) {
    return o.number - number;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o)
      return true;
    if (o == null || getClass() != o.getClass())
      return false;
    Compare compare = (Compare)o;
    return number == compare.number;
  }

  @Override
  public int hashCode() {
    return Objects.hash(number);
  }
}

@Test
@DisplayName("Comparable 을 구현한 객체의 정렬")
void sort_object() {
  List<Compare> compares = IntStream.range(0, 10)
    .mapToObj(Compare::new).collect(toList());

  List<Compare> expected = new ArrayList<>();
    
  for (int i = 9; i >= 0; i--) {
    expected.add(new Compare(i));
  }

  Collections.sort(compares);
    
  assertEquals(expected, compares);
}

예시를 들기위해 수의 크기에 따른 내림차순 정렬로 구현하였습니다.

Comparable 을 구현하지 않은 클래스를 Arrays 나 Collections 의 sort 메서드로 정렬하려고 하면 ClassCastException 이 발생합니다.

@Test
@DisplayName("Comparable 을 구현하지 않은 객체는 예외 발생")
void sort_object_exception() {
  assertThrows(ClassCastException.class,
    () -> Arrays.sort(IntStream.range(0, 10)
          .mapToObj(NotCompare::new).toArray()));
}

Comparator

Comparator 는 명시적인 정렬을 하고 싶을 때 사용하는 인터페이스입니다.

compare 메서드를 구현함으로써 정렬을 정의합니다. 클래스에 정의하는 것보다는 정렬시 함수로 넘기는 것을 더 선호합니다(개인적인 의견입니다)

@Test
@DisplayName("comparator 통해 정렬")
void sort_comparator_implements() {
  List<Integer> numbers = Arrays.asList(3, 9, -1, 7, 6);
  List<Integer> expected = Arrays.asList(9, 7, 6, 3, -1);

  // comparator 를 람다로 구현하여 넘겨주었음
  numbers.sort((n1, n2) -> n2 - n1);
  assertEquals(expected, numbers);
}

Comparator 를 람다로 정의할 때, 여러 조건을 넘기도록 구현할 수 있습니다.

@Test
@DisplayName("함수를 통해 정렬조건을 여러개 넣기")
void sort_thenComparing() {
  List<Person> people = Arrays.asList(
    new Person(10, "bcd"),
    new Person(12, "csb"),
    new Person(10, "abc")
  );
  List<Person> expected = Arrays.asList(
    new Person(10, "abc"),
    new Person(10, "bcd"),
    new Person(12, "csb")
  );

  // thenComparing 을 통해 조건을 이어감
  people.sort(Comparator.comparing(Person::getAge).thenComparing(Person::getName));
  assertEquals(expected, people);
}

 

해당 테스트코드는 github 에 수록되어있습니다.

'Java' 카테고리의 다른 글

EnumMap 살펴보기  (0) 2021.01.05
PriorityQueue 살펴보기  (0) 2021.01.04
resource file 읽기  (0) 2018.12.18
eclipse task tag 사용(TODO)  (0) 2018.12.18
java applicaiton logback 설정  (0) 2018.12.18