자바를 사용하다보면 객체를 정렬하고 싶은 경우가 많이 생긴다.
하지만 객체 안에 어떤 데이터를 기준으로 정렬할지 컴퓨터는 알 수 없다.
이 정렬 기준을 정해주는 것이 Comparable과 Comparator다.
즉, 객체를 비교할 수 있도록 해준다.
Comparable & Comparator ( API 문서 )
Comparable과 Comparator는 모두 인터페이스다.
즉, 사용하기 위해서는 인터페이스 내에 있는 추상 메서드를 반드시 오버라이딩 해야 한다.
메서드를 확인하기 위해서 공식 API 문서를 참고하자.
살펴보면 compareTo(T o) 메서드 하나가 존재한다.
사용하려면 compareTo(T o) 메서드를 오버라이딩 하면 된다.
살펴보면 Comparable 인터페이스와 달리, 메서드가 많이 존재한다는 사실에 어지러울 수 있다.
하지만 우리가 실질적으로 구현 할 메서드는 compare(T o1, T o2) 단 하나다.
다른 메서드들은 static, default 메서드 이기 때문에 오버라이딩이 강제되지 않는다.
Comparable과 Comparator란?
Comparable과 Comparator는 객체를 비교할 수 있도록 해준다고 했다.
그렇다면 우리는 언제 객체를 비교해야 할까?
예시로 내가 학생 정보를 담고 있는 클래스를 만들었다고 생각해보자.
public class Student {
private int stdNum; //학번
private String name; //이름
private int kor; // 국어점수
private int eng; // 영어점수
private int mat; // 수학점수
public Student(int stdNum, String name, int kor, int eng, int mat) {
this.stdNum = stdNum;
this.name = name;
this.kor = kor;
this.eng = eng;
this.mat = mat;
}
}
다음 Student 클래스는 [ 학번, 이름, 국어 점수, 영어 점수, 수학 점수 ] 정보를 가지고 있다.
public class Test {
public static void main(String[] args) {
Student std1 = new Student(1, "현기", 100, 90, 80); // 학생1 객체 생성
Student std2 = new Student(2, "철수", 80, 70, 60); // 학생2 객체 생성
Student std3 = new Student(3, "영희", 90, 50, 100); // 학생3 객체 생성
}
}
3명의 학생 객체를 생성했다.
이 세 명의 학생 객체를 어떻게 비교하여 정렬할 수 있을까?
학번을 기준으로 비교할지, 어떤 점수를 기준으로 비교할지 알 수 없다.
사용자가 기준을 정해주지 않는 이상 컴퓨터는 어떤 객체가 더 높은 우선순위를 갖는지 판단할 수 없다.
이러한 문제점을 해결하기 위해 Comparable 또는 Comparator을 사용한다.
즉, 객체를 비교할 기준을 잡아준다는 것이다.
⦁ 그렇다면 이 둘은 어떤 차이가 존재할까?
Comparable의 compareTo(T o) 메서드는 매개변수가 한 개였고,
Comparator의 compare(T o1, To2) 메서드는 매개변수가 두 개였다.
=
Comparable은 "자기 자신과 매개변수 객체를 비교"하고,
Comparator는 "두 매개변수 객체를 비교"한다.
일반적으로 Comparable은 기본적인 정렬 기준을 설정하고 싶을 경우 사용하고
Comparator는 특별한 정렬 기준을 설정하고 싶을 때 사용한다.
+ 알아두면 좋은 지식
Comparable의 패키지 : java.lang.Comparable
자바에서 제공하는 정렬이 가능한 클래스들은 모두 Comparable 인터페이스를 구현하고 있으며,
정렬시에 Comparable의 구현 내용에 맞춰 정렬이 수행된다.
Comparator의 패키지 : java.utill.Comparator
java.utill 에 존재하므로 import를 해줘야 한다. 주로 익명 함수를 사용한다고 한다.
컬렉션의 다양한 메서드에 매개변수로 활용이 가능하기 때문에 매우 유용하다.
예 : Arrays.sort(array, myComparator), Collections.sort(list, myComparator)
1. Comparable 사용 방법
1. Comparable 인터페이스 상속
인터페이스니까 상속받아 구현해야 한다. 만약 인터페이스와 제네릭에 대해 모른다면
https://hyunki99.tistory.com/12와 https://hyunki99.tistory.com/16 을 참고하자.
2. compareTo 메서드 오버라이딩
@Override
public int compareTo(Student o) {
// 자신의 kor이 o의 kor보다 크다면 양수
if (this.kor > o.kor) {
return 1;
// 자신의 kor이 o의 kor보다 작다면 음수
} else if (this.kor < o.kor) {
return -1;
}
// 자신과 o의 kor이 같으면 0
return 0;
}
compareTo는 값을 비교해서 정수를 반환해야 한다.
위 코드는 "자기 자신"의 학번과 "상대방"의 학번 비교해 대소 관계를 파악한 메서드다.
조건문을 통해 <, >, ==을 활영하여 대소비교를 하고 그에 따라 1, 0, -1을 반환하는 방식이 이해하기도 쉽고
가장 정석적인 방법이다.
+ 알아두면 좋은 지식
1, 0, -1 을 리턴하면 되므로 따로 조건문을 걸지 않고
return this.kor- o.kor;
처럼 사용할 수도 있지만, 해당 자료형의 표현 범위를 벗어나게 되면 오버플로우, 언더플로우가 발생할 여지가 존재하므로 반드시 확인하고 사용해야 한다. 기본 타입에 대해 예외를 확인하기 어렵다면 정석적인 방법으로 대소비교를 해주는 것이 안전하고 일반적으로 권장되는 방식이다.
3. 결과 화면
compareTo를 오버라이딩 함으로 인해서 객체의 대소 관계 비교가 가능하게 되었다. (기준을 정해주었다.)
이를 응용한다면 드디어 객체 간의 정렬이 가능하게 된 셈이다.
지금은 학번을 기준으로 비교했지만, 메서드 안에 비교할 변수만 변경해 주면
국어 점수, 영어 점수 등 학생 클래스에 존재하는 모든 데이터를 비교할 수 있다.
⦁전체코드 보기
class Student implements Comparable<Student>{
private int stdNum; //학번
private String name; //이름
private int kor; // 국어점수
private int eng; // 영어점수
private int mat; // 수학점수
public Student(int stdNum, String name, int kor, int eng, int mat) {
this.stdNum = stdNum;
this.name = name;
this.kor = kor;
this.eng = eng;
this.mat = mat;
}
@Override
public int compareTo(Student o) {
// 자신의 학번이 o의 학번보다 크다면 양수
if (this.stdNum > o.stdNum) {
return 1;
// 자신의 학번이 o의 학번보다 작다면 음수
} else if (this.stdNum < o.stdNum) {
return -1;
}
// 자신과 o의 학번이 같으면 0
return 0;
}
}
public class Test {
public static void main(String[] args) {
Student std1 = new Student(1, "현기", 100, 90, 80); // 학생1 객체 생성
Student std2 = new Student(2, "철수", 80, 70, 60); // 학생2 객체 생성
Student std3 = new Student(3, "영희", 90, 50, 100); // 학생3 객체 생성
int isBig = std1.compareTo(std2); // a자기자신과 b객체를 비교한다.
if(isBig > 0) {
System.out.println("a객체가 b객체보다 큽니다.");
}
else if(isBig == 0) {
System.out.println("두 객체의 크기가 같습니다.");
}
else {
System.out.println("a객체가 b객체보다 작습니다.");
}
}
}
2. Comparator 사용 방법
전반적인 구현 형태는 Comparble과 비슷하다.
자기 자신과 비교 vs 매개변수로 들어오는 두 객체를 비교의 차이가 있을 뿐이다.
특별한 정렬기준을 정할 때 사용한다고 했으니, 학번말고 국어 점수를 기준으로 구현해보자.
1. Comparator 인터페이스 상속
java.util 패키지에 존재하므로 import가 필요하다.
2. compare 메서드 오버라이딩
@Override
public int compare(Student o1, Student o2) {
// 자신의 kor이 o의 kor보다 크다면 양수
if (o1.kor > o2.kor) {
return 1;
// 자신의 kor이 o의 kor보다 작다면 음수
} else if (o1.kor < o2.kor) {
return -1;
}
// 자신과 o의 kor이 같으면 0
return 0;
}
3. 결과 화면
Comparable과 크게 다르지 않다. 다만 매개변수를 2개 넣어주었을 뿐이다.
이 상태로 정렬에 활용하면 오름차순 정렬 구현이 가능하다. 만약 내림차순 정렬이 하고 싶다면
compare 메서드의 부등호 방향만 바꿔주면 된다.
(부등호의 방향이 많이 헷갈린다..)
⦁전체코드 보기
import java.util.Comparator;
class Student implements Comparator<Student>{
private int stdNum; //학번
private String name; //이름
private int kor; // 국어점수
private int eng; // 영어점수
private int mat; // 수학점수
public Student(int stdNum, String name, int kor, int eng, int mat) {
this.stdNum = stdNum;
this.name = name;
this.kor = kor;
this.eng = eng;
this.mat = mat;
}
@Override
public int compare(Student o1, Student o2) {
// 자신의 kor이 o의 kor보다 크다면 양수
if (o1.kor > o2.kor) {
return 1;
// 자신의 kor이 o의 kor보다 작다면 음수
} else if (o1.kor < o2.kor) {
return -1;
}
// 자신과 o의 kor이 같으면 0
return 0;
}
}
public class Test2 {
public static void main(String[] args) {
Student std1 = new Student(1, "현기", 100, 90, 80); // 학생1 객체 생성
Student std2 = new Student(2, "철수", 80, 70, 60); // 학생2 객체 생성
Student std3 = new Student(3, "영희", 90, 50, 100); // 학생3 객체 생성
int isBig = std1.compare(std2, std3); // a자기자신과 b객체를 비교한다.
if(isBig > 0) {
System.out.println("a객체가 b객체보다 큽니다.");
}
else if(isBig == 0) {
System.out.println("두 객체의 크기가 같습니다.");
}
else {
System.out.println("a객체가 b객체보다 작습니다.");
}
}
}
ArrayList에서 Comparator을 이용한 정렬 예제
Comparator는 특별한 정렬 기준을 설정하고 싶을 경우 사용한다고 했다.
가장 많이 사용되는 자료구조인 ArrayList를 활용해서 Student 객체의 국어 점수를 기준으로 정렬하는
방법에 대해 알아보자.
정렬은 실제 구현이나 코딩 테스트 등에서 정말 많이 사용되기 때문에 잘 알아두면 도움이 많이 될 것이다.
⦁전체코드 보기
< Student 클래스 >
class Student{
private int stdNum; //학번
private String name; //이름
private int kor; // 국어점수
private int eng; // 영어점수
private int mat; // 수학점수
public Student(int stdNum, String name, int kor, int eng, int mat) {
this.stdNum = stdNum;
this.name = name;
this.kor = kor;
this.eng = eng;
this.mat = mat;
}
// get set 추가
public int getKor() {
return kor;
}
public void setKor(int kor) {
this.kor = kor;
}
}
< KorComparator > ( 국어 점수 내림차순 )
class KorComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
if (o1.getKor() < o2.getKor()) {
return 1;
} else if (o1.getKor() > o2.getKor()) {
return -1;
}
return 0;
}
}
< Main >
public class Test2 {
public static void main(String[] args) {
ArrayList<Student> arr = new ArrayList<>();
arr.add(new Student(1, "현기", 100, 90, 80));
arr.add(new Student(2, "철수", 80, 70, 60));
arr.add(new Student(3, "영희", 90, 50, 100));
arr.add(new Student(4, "대기", 60, 60, 80));
arr.add(new Student(5, "근영", 100, 100, 70));
// 정렬 전
for(Student i : arr) {
i.disp();
}
// Comparator 인터페이스를 구현한 클래스를 매개변수로 활용하여 정렬
Collections.sort(arr, new KorComparator());
// 정렬 후
System.out.println("=============== 정렬 후");
for (Student i : arr) {
i.disp();
}
}
}
참고 문헌 : Do it! 자바 완전정복
참고 블로그 : https://st-lab.tistory.com/243
'Java > 자바 이론' 카테고리의 다른 글
[Java] 자바 두 개의 큐(Queue)로 스택(Stack) 구현하기 (0) | 2022.07.17 |
---|---|
[Java] 자바 두 개의 스택(Stack)으로 큐(Queue) 구현하기 (0) | 2022.07.17 |
[Java] 자바 제네릭(Generic)이란? (Feat. 오토박싱) (0) | 2022.07.09 |
[Java] 자바 컬렉션(Collection) 프레임워크란? (0) | 2022.07.06 |
[Java] 자바 쓰레드(Thread) & 동기화 (0) | 2022.07.06 |