java, 비교와 정렬 - Comparable, Comparator, Arrays.sort 외
자바에서 비교를 위해
- 기본타입의 경우 단순하게 비교가 되지만 객체의 경우 비교하기가 어렵다.
- 자바에서는 Comparable 와 Comparator를 통해 비교를 위한 명확하고 확장 가능한 인터페이스를 제공한다.
if(10>20) {} // 가능
if(userA > userB){} // 불가능
Comparator
- Comparator는 기본 타입에 대한 래퍼 클래스의 비교가 가능하지만 특정 객체에 특화된 비교를 제공한다.
-
다음과 같이 User라는 클래스가 있고 그것을 다음과 같이 Comparator를 작성하여 비교해봤다.
- User
@Data
public class User {
private final String name;
private final int money;
private final int age;
}
- money 와 age 를 기준으로 비교한다.
- Comparator
을 사용하여 특정 객체에 특화된 비교가 가능하다. - 비교는 좌항과 우항의 차이를 통해 비교한다. 양수가 나올 경우 좌항이 크다고 보며 음수가 나올 경우 우항이 크다고 본다.
Comparator<User> compareWithMoney = Comparator.comparingInt(User::getMoney);
Comparator<User> compareWithAge = Comparator.comparingInt(User::getAge);
User older = new User("kim", 10, 20);
User richer = new User("lee", 30, 10);
compareWithMoney.compare(richer, older); // 양수
compareWithMoney.compare(older, richer); // 음수
compareWithAge.compare(older, richer); // 양수
compareWithAge.compare(richer, older); // 음수
Comparable
- Comparable을 구현하면 Comparator 등 별도로 비교를 위한 코드를 작성하지 않아도 된다.
int compareTo(Object o);
을 오버라이딩 해야 한다.-
Assertions#isSorted 가 Comparable 을 기준으로 한다.
- UserOverFlow
public class UserOverFlow extends User implements Comparable<User>{
public UserOverFlow(String name, int money, int age) {
super(name, money, age);
}
@Override
public int compareTo(User o) {
return getAge() - o.getAge();
}
}
- 아래와 같이 Comparator가 없어도 정렬 가능하다.
UserOverFlow older = new UserOverFlow("old", 10, 20);
UserOverFlow younger = new UserOverFlow("young", 30, 10);
// 정상적으로 정렬 됨
List<UserOverFlow> list = Arrays.asList(younger, older);
Collections.sort(list);
System.out.println(list);
- 하지만 compareTo을
getAge() - o.getAge()getAge() - o.getAge()
로 구현하였다. 이는 오버플로우로부터 안전하지 않다. - 게다가 테스트를 위한 org.assertj.core.api.Assertions의 메서드가 이 문제를 잡아내지 못한다!
UserOverFlow older = new UserOverFlow("old", 10, Integer.MAX_VALUE);
UserOverFlow younger = new UserOverFlow("young", 30, -1); // 나이가 음수일 수는 없지만 ㅠ
// 우항이 커서 음수를 리턴. 비정상적으로 동작.
older.compareTo(younger); // 음수
// 정렬 결과도 비정상
List<UserOverFlow> list = Arrays.asList(younger, older);
Collections.sort(list);
// true를 반환! 헐! 믿었던 junit이 동작하지 않음! Comparable 을 잘 구현해야 한다!
Assertions.assertThat(list).isSorted();
- 아래와 같이 변경하였고 정상 동작한다.
@Override
public int compareTo(User u) {
return Integer.compare(getAge(), u.getAge());
}