Comparable, Comparator, 배열을 정렬하는 여러가지 방법
JAVA에서 배열을 정렬을 하는 방법은 여러가지가 있다.
먼저 기본 자료형인 int, double, String 등의 배열은
Arrays.sort() 메서드를 호출하면 된다.
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
int[] intArray = {5, 2, 1, 4, 3};
Arrays.sort(intArray);
// 1, 2, 3, 4, 5로 정렬 된다.
}
}
동적배열인 List는 Collections.sort() 메서드를 호출하면 된다.
import java.util.*;
public class Example {
public static void main(String[] args) {
List<Integer> intList = Arrays.asList(5, 2, 4, 1, 3);
Collections.sort(intList);
// 1, 2, 3, 4, 5로 정렬된다.
}
}
Arrays.sort()는 파라미터로 T [] 인 기본 배열을 받고 Collections.sort()는 파라미터로 List 인 List를 받는다.
그렇다면 sort() 메서드에서 기본 자료형들은 어떻게 비교해서 정렬을 할 수 있을까 ?
바로 Comparable 인터페이스를 구현했기 때문이다.
interface Comparable<T> {
int compareTo(T obj)
}
즉, Comparable 인터페이스의 구현해서 compareTo 메서드를 오버라이딩한 클래스는
sort() 메서드를 통해 비교가 가능한 것이다. 그렇다면 임의로 만든 클래스의 객체 배열은 어떻게 정렬할까?
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
Person[] people = {new Person(30, "홍길동"), new Person(20, "김철수"), new Person(25, "김영희")};
Arrays.sort(people);
// 정렬 실패.
}
}
class Person {
int age;
String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
}
위의 같은 코드를 실행한다면
Exception in thread "main" java.lang.ClassCastException: Person cannot be cast to [java.lang.Comparable] 라는 오류가 발생한다.
Person 클래스는 java lang.Comparable에 캐스팅 될 수 없다는 오류이다.
한마디로 Person 클래스는 Comparable 인터페이스를 구현하지 않았기 때문에
에러가 난 것이고 Arrays.sort() 메서드로 정렬을 할 수 없는 것이다.
Person 클래스에 Comparable 인터페이스를 구현하고 compareTo메서드를 오버라이딩하면
Arrays.sort() 메서드로 정렬을 할 수 있다.
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
Person[] people = {new Person(30, "홍길동"), new Person(20, "김철수"), new Person(25, "김영희")};
Arrays.sort(people);
// 나이 순 정렬 성공 {20, 김철수}, {25, 김영희}, {30, 홍길동}으로 정렬된다.
}
}
class Person implements Comparable<Person>{
int age;
String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
/*
* 나이를 기준으로 정렬하고 나이가 같다면 이름 순으로 정렬한다.
*/
@Override
public int compareTo(Person other) {
int r = this.age - other.age;
if (r == 0) {
r = this.name.compareTo(other.name);
}
return r;
}
}
하지만 이렇게 Comparable 인터페이스를 구현한 클래스라면 compareTo 메서드를 여러개 재정의 할 수 없으니 비교기준을 한가지 밖에 가질 수 없다.
위의 코드처럼 나이를 기준으로 먼저 비교해서 정렬하고
나이가 같다면 이름 순으로 정렬하는 compareTo 메서드를 재정의했다면
이름 순으로 먼저 비교하고 싶을 때는 난감한 경우가 생긴다.
그럴 땐 Comparator 인터페이스를 구현하면 된다.
interface Comparator<T> {
int compare(T obj1, T obj2);
}
Comparator 인터페이스는 Comparable 인터페이스와 다르게
compare 메서드를 오버라이딩 해야한다.
compare 메서드는 compareTo 메서드와 다르게 파라미터 변수가 두 개이다.
compareTo 메서드는 파라미터 한개로 객체를 받아서this 키워드를 사용해서 비교했지만
compare 메서드는 파라미터 두개로 두개의 객체를 받아서서로 비교해주면 된다.
Comparator 인터페이스는 비교 기준이 여러 개 일때 사용하는 인터페이스기에
비교할 클래스에 구현하는 것이 아니라
따로 클래스를 구현해서 만들어 주는 것이 좋다.
import java.util.Arrays;
import java.util.Comparator;
public class Example {
public static void main(String[] args) {
Person[] people = {new Person(30, "홍길동"), new Person(20, "김철수"), new Person(25, "김영희")};
Arrays.sort(people, new PersonAgeComparator()); // 나이 기준 정렬
// {20, 김철수}, {25, 김영희}, {30, 홍길동}으로 정렬된다.
Arrays.sort(people, new PersonNameComparator()); // 이름 기준 정렬
// {25, 김영희}, {20, 김철수}, {30, 홍길동}으로 정렬된다.
}
}
class Person {
int age;
String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
}
class PersonAgeComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
int r = p1.age - p2.age;
if (r == 0) {
r = p1.name.compareTo(p2.name);
}
return r;
}
}
class PersonNameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
int r = p1.name.compareTo(p2.name);
if (r == 0) {
r = p1.age - p2.age;
}
return r;
}
}
정렬을 한 번하고 Comparator 클래스들을 재 사용할 것이 아니라면 굳이 class를 만들 필요 없이 무명 클래스와 람다 익스프레션을 활용하면 된다.
이 방법들이 코드 양이 훨씬 줄어든다.
-
무명클래스
import java.util.Arrays; import java.util.Comparator; public class Example { public static void main(String[] args) { Person[] people = {new Person(30, "홍길동"), new Person(20, "김철수"), new Person(25, "김영희")}; Arrays.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { int r = p1.age - p2.age; if (r == 0) { r = p1.name.compareTo(p2.name); } return r; } }); // 나이 기준 정렬 // {20, 김철수}, {25, 김영희}, {30, 홍길동}으로 정렬된다. Arrays.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { int r = p1.name.compareTo(p2.name); if (r == 0) { r = p1.age - p2.age; } return r; } }); // 이름 기준 정렬 // {25, 김영희}, {20, 김철수}, {30, 홍길동}으로 정렬된다. } }
-
람다 익스프레션
import java.util.Arrays; import java.util.Comparator; public class Example { public static void main(String[] args) { Person[] people = {new Person(30, "홍길동"), new Person(20, "김철수"), new Person(25, "김영희")}; Arrays.sort(people, (p1, p2) -> { int r = p1.age - p2.age; if (r == 0) { r = p1.name.compareTo(p2.name); } return r; }); // 나이 기준 정렬 // {20, 김철수}, {25, 김영희}, {30, 홍길동}으로 정렬된다. Arrays.sort(people, (p1, p2) -> { int r = p1.name.compareTo(p2.name); if (r == 0) { r = p1.age - p2.age; } return r; }); // 이름 기준 정렬 // {25, 김영희}, {20, 김철수}, {30, 홍길동}으로 정렬된다. } }
-
Stream API
import java.util.Arrays; public class Example { public static void main(String[] args) { Person[] people = {new Person(30, "홍길동"), new Person(20, "김철수"), new Person(25, "김영희")}; people = Arrays.stream(people) .sorted((p1, p2) -> { int r = p1.age - p2.age; if (r == 0) { r = p1.name.compareTo(p2.name); } return r; }) .toArray(Person[]::new); // 나이 기준 정렬 // {20, 김철수}, {25, 김영희}, {30, 홍길동}으로 정렬된다. people = Arrays.stream(people) .sorted((p1, p2) -> { int r = p1.name.compareTo(p2.name); if (r == 0) { r = p1.age - p2.age; } return r; }) .toArray(Person[]::new); // 이름 기준 정렬 // {25, 김영희}, {20, 김철수}, {30, 홍길동}으로 정렬된다. } }
'Java' 카테고리의 다른 글
[Java 8] 메서드 레퍼런스(Methods Reference) (0) | 2020.09.15 |
---|---|
[Java] 유용한 Math 메소드 (0) | 2020.07.05 |
Map의 유용한 인터페이스 (0) | 2020.06.19 |
자바 잡학사전 (0) | 2020.02.12 |
deque [java] (0) | 2019.09.19 |