본문으로 바로가기

Java 8 Comparator

category 개발이야기/Java 2017. 9. 14. 22:46
반응형

이번에는 Comparator interface에 대해서 다룹니다.

정렬을 하기 위해서 구현하는 클래스 이면 Collection.sort()를 통하여 list를 소팅하거나, TreeMap같이 정렬이 필요한 자료구조에 Comparable과 같이 구현하여 많이 사용합니다.

Java 8 에서는 좀더 간단한 방법으로 comparator를 구현할 수 있습니다.

먼저 sorting에 사용할 예제 클래스는 아래와 같습니다.

public class Person {
    private final String mName;
    private final int mAge;

    public Person (String name, int age) {
        mName = name;
        mAge = age;
    }

    public String getName() {
        return mName;
    }

    public int getAge() {
        return mAge;
    }

    public int ageDiff(final Person other) {
        return age - other.age;
    }
   
    public String toString() {
        return mName + " | " + mAge;
    }
}

예제 클래스는 어렵지 않습니다.

Person 클래스에 이름과 나이를 멤버로 갖고, ageDiff()란 비교 함수가 존재합니다.


예제를 Kotlin으로?

예제 코드를 넣다보니, kotlin으로 작성하면 좀더 심플하겠다란 생각이 들어서 주제와는 맞지 않지만 예제 클래스를 Kotlin으로 작성해 보죠~

class Person (val mName: String, val mAge: Int) {

    fun ageDiff(val other: Person): Int = mAge - other.mAge
    fun toString(): String = mName + " | " + mAge
}

허..써놓고 보니 같은 클래스인데 너무 차이나네요. (엄청 심플함)

Lambda expression을 이용한 정렬

본격적으로 람다식을 이용해서 정렬을 해 보겠습니다.

먼저 해당 person객체를 갖는 리스트를 만들겠습니다.

final List<Person> people = Arrays.asList(
                   new Person("홍길동", 18),
                   new Person("임꺽정", 23),
                   new Person("민수", 13),
                   new Person("영희", 14),
                   new Person("철수", 15)); 

이제 stream을 통해서 sorting해 봅니다.

List<Person> ageASC = people.stream()
                      .sorted((person1, person2) -> person1.ageDiff(person2))
                      .collect(toList());   

stream을 이용하여 간단히 sorting 했습니다.

sorted() 함수는 인자로 Comparator 를 갖습니다.

그러니 함수 signature는 (T,V) -> R 정도로 보시면 됩니다. (BiFunction과 같습니다.)


여기서 Comparator는 method reference로 치환할수 있습니다.

List<Person> ageASC = people.stream()
                      .sorted(Person::ageDiff)
                      .collect(toList());

엄청 단순해 졌습니다.


역순정렬을 하고 싶다면 어떻게 해야 할까요?

위 예제에서 person1과 person2를 뒤집으면 됩니다만 method reference를 순서를 바꿀수 없습니다.

따라서 reversed() method를 사용합니다.

// 방법1
List<Person> ageDESC = people.stream()
                      .sorted((person1, person2) -> person2.ageDiff(person1))
                      .collect(toList());
// 방법2
List<Person> ageDESC = people.stream()
                      .sorted((Person::ageDiff).reversed())
                      .collect(toList());


최대, 최소값 구하기

정렬된 자료중에 최대값 하나만 뽑고 싶을수도 있고, 최소값 하나만 뽑을수도 있습니다.

또한 2차,3차 정렬을 하고 싶은경우도 있습니다.

다행이도 이런 경우 대한 API역시 지원합니다

나이가 적은 한사람만 뽑고 싶은 경우와 나이가 제일 많은 사람을 뽑고 싶은 경우

people.stream()
      .min(Person::ageDiff)
      .ifPresent(System.out::println)

people.stream()
      .max(Person::ageDiff)
      .ifPresent(System.out::println)

생각외로 엄청 간단합니다. 그쵸??

mix(), max() 함수는 둘다 Comparator를 인자로 받습니다. 따라서 ageDiff()함수가 method reference로 사용되어 들어갑니다.


또 한가지 min()과 max()의 return값은 Optional 입니다.

따라서 Optional 함수인 ifPresent()를 통해 결정된 값을 출력가능합니다.


comparing

Comparator는 comparing()함수를 지원합니다. 예상컨데 아마도 default method로 추가되었겠죠?

comparing은 Function<T,R>을 인자로 받습니다.

잠깐! 갑자기 등장하는 Function이라던가..Optional이란 단어를 모른다면, 람다식부터 보고 오셔야 합니다.

comparing 함수를 이용해서 간단하게 정렬을 바꿔보겠습니다.

이번엔 이름 알파벳 순으로 정렬을 진행합니다.

people.stream()
      .sorted(comparing(person -> person.getName()));

comparing()은 sorted()안에 쓰였습니다.

위에서 sorted()는 인자로 Comparator를 갖는다고 언급했습니다.

따라서 comparing()은 Function을 인자로 받고 Comparator를 return합니다.


그리고 친절하게도 Comparator는 thenComparing()이란 함수도 지원합니다.

이 함수 역시 Function을 인자로 받아 Comparator를 반환합니다.


그럼 thenComparing()을 이용하여 2차 정렬을 진행해 봅니다.

Function<Person, Integer> firstSort = person -> person.getAge();
Function<Person, String> secondSort = person -> person.getName();

people.stream().sorted((firstSort).thenComparing(secondSort));

이렇게 하면 1차 정렬을 나이로 한 후에 2차정렬은 이름으로으로 진행된 결과를 같습니다.


comparator는 매우 자주 사용하는 interface입니다.

Java 8 에 추가된 default method를 활용하면 좀더 간결하고 의미전달이 쉬운 코드를 작성할 수 있습니다.


제가 자주 사용하는 method 중에는 naturalOrder()란 함수도 있으니, 이는 직접 찾아보고 사용하시길 바랍니다. (매우 유용한 함수 입니다.)


참고: https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html

반응형

'개발이야기 > Java' 카테고리의 다른 글

람다의 내부동작 #2  (0) 2017.09.26
람다의 내부동작 #1  (0) 2017.09.24
Java 8 String join  (2) 2017.09.13
Java 8 Lambda Expression - 람다식 #4  (0) 2017.09.12
Java 8 Lambda Expression - 람다식 #3  (0) 2017.09.12