ejyoo's 개발 노트

Comparable, Comparator 차이 본문

BackEnd/Java

Comparable, Comparator 차이

ejyoovV 2021. 3. 4. 08:35

Q. 질문 

Comparable은 정해진 기준으로 정렬할 때 사용하고 Comparator는 사용자가 원하는 기준을 커스텀해서 사용한다.

Comparable도 리턴값만 수정하면 사용자가 원하는 대로 값을 설정할수 있는데 둘의 차의점을 모르겠다.

 

Comparable 인터페이스
Comparator 인터페이스

💡 같은점과 다른점

1) 같은점

 - 인터페이스이고 int 값을 반환한다. 

 - 정렬하는 것이다. (정렬의 기준을 정의한다.)

다른점 : 정렬 기준이 일반적인가? 아닌가? / compareTo(Object o)메서드 구현이냐 compare(Object o1, Object o2)메서드를 구현하냐?

 

💡 정의

  자바에서 객체를 정렬할 때 일정한 기준이 필요하다.

  1. Comparable
    • 객체간의 일반적인 정렬이 필요할때
    • 기준 1가지를 가지고 그에 대한 정렬을 하고 싶을 때
      ex) 학점을 기준으로 오름차순 or 내림차순
    • compareTo() 메서드를 구현한다.
    • Default. 오름차순 기준
    • Integer, Double 클래스 : 오름차순 정렬 / String 클래스 : 사전순 정렬
  2. Comparator
    • 객체간의 특정한 정렬이 필요할 때
    • Comparable과 다르게 사용자 정의조건임. 즉 요구사항에서 주어진 특정 기준을 가지고 정렬
      ex) 학점이 같다면 이름순으로 정렬
    • compare() 메서드를 구현한다.

📝 Comparable은 기본적인 정렬 위주일 때 사용하고 특정한 규칙을 사용해 정렬을 하고 싶을때 Comparator를 사용한다.

 

 

위의 정의를 가지고 한가지 문제를 풀어보려고 한다.

 

 

 

💡 문제

더보기

학번, 이름, 국어점수, 영어점수, 수학점수, 총점, 등수를 멤버로 갖는
Student클래스를 만든다.
생성자는 학번, 이름, 국어, 영어, 수학 점수만 매개변수로 받아서 처리한다.
  
이 Student객체들은 List에 저장하여 관리한다.
List에 저장된 데이터들을 학번의 오름차순으로 정렬하여 출력하는 부분과
총점의 역순으로 정렬하는 부분을 프로그램 하시오.
(총점이 같으면 학번의 내림차순으로 정렬되도록 한다.)
(학번 정렬기준은 Student클래스 자체에서 제공하도록 하고,
총점 정렬기준은 외부클래스에서 제공하도록 한다.)

💡 문제분석

1. Student 클래스를 생성한다.

2. Student 클래스 내 멤버는 학번, 이름, 국어점수, 영어점수, 수학점수, 총점, 등수 를 가진다.

3. 별도의 VO(Value Object)를 생성하지 않을 것이므로, Student를 VO처럼 사용한다.

4. 학번(String), 이름(String), 국어점수(int), 영어점수(int), 수학점수(int), 총점(int), 등수(int)로 정의한다.

5. Student 클래스 내 멤버변수의 접근제어자는 private로 제한한다.

6. 제한한 멤버변수에 대한 Setter, Getter를 생성한다.

7. Student의 생성자 생성 시 파라미터로 학번, 이름, 국어, 영어, 수학을 받는다.

8. 생성자를 사용하여 각 값을 초기화 한 뒤 List에 저장한다.

9. Student는 기본적으로 '학번'을 기준으로 오름차순 정렬한다. (기본정렬)

10. 총점의 역순으로 정렬하되, 총점이 같은 경우 '학번'을 기준으로 내림차순 정렬한다. (조건정렬)

11. 학번 정렬 기준(기본정렬)은 Student 클래스 자체에서 제공한다.

12. 총점(조건)이 같은 경우, 학번 정렬기준(조건정렬) 외부 클래스를 별도로 생성하여 제공한다.

13. 출력 시 toString을 오버라이딩하여 재정의한다.

 

📝 문제 분석 결과 

기본정렬에 대한 부분은 Comparable의 compareTo 메서드를 사용하여 정의하고

조건정렬에 대한 부분은 Comparator의 compare 메서드를 사용하여 정의한다.

 

 

💡 문제풀이

1. Student 클래스를 생성한다.

2. Student 클래스 내 멤버는 학번, 이름, 국어점수, 영어점수, 수학점수, 총점, 등수 를 가진다.

3. 별도의 VO(Value Object)를 생성하지 않을 것이므로, Student를 VO처럼 사용한다.

4. 학번(String), 이름(String), 국어점수(int), 영어점수(int), 수학점수(int), 총점(int), 등수(int)로 정의한다.

5. Student 클래스 내 멤버변수의 접근제어자는 private로 제한한다.

6. 제한한 멤버변수에 대한 Setter, Getter를 생성한다.

7. Student의 생성자 생성 시 파라미터로 학번, 이름, 국어, 영어, 수학을 받는다.

8. 생성자를 사용하여 각 값을 초기화 한 뒤 List에 저장한다.

9. Student는 기본적으로 '학번'을 기준으로 오름차순 정렬한다. (기본정렬)

기본정렬이므로 Comparable의 compareTo() 메서드를 사용한다.(오름차순)

Collections.sort 호출 시 List 묶음을 보내준다.

List 와 똑같은 형태를 파라미터에 넣고 return 시 오름차순 정렬이므로 아무것도 하지않고

현재 객체 값에 존재하는 compareTo메서드에(String.class에 존재) 파라미터로 넘어온 객체 값을 삽입하여 호출한다.

호출하면 자동적으로 오름차순으로 정렬이 된다.

 

10. 총점의 역순으로 정렬하되, 총점이 같은 경우 '학번'을 기준으로 내림차순 정렬한다. (조건정렬)

12. 총점(조건)이 같은 경우, 학번 정렬기준(조건정렬) 외부 클래스를 별도로 생성하여 제공한다.

특별한 조건이 있는 정렬이므로 Comparable의 compare메서드를 사용한다.

외부 클래스를 별도로 생성하여 코딩해야하므로 귀찮아서 Student클래스 내에 조건 정렬을 위한 클래스를 생성하였다.

compare 메서드를 사용한다. compare 메서드는 리턴 타입이 int 이므로 양수 음수 0 값만 가질 수있다.

compare에는 비교 대상이 2개가 들어간다. 

생각할 때, 그냥 맨 앞에서부터 값 2개를 비교한다고 가정했을 때,

앞과 뒤의 값을 오름차순 정렬할건지 내림차순 정렬할건지 정하면 된다.

 

여기에서 조건이 총점이 같은경우라고 했다.

조건비교 if를 사용하여 총점이 같은 경우를 만들어 준뒤 int 형태로 리턴해주면 된다.

이때 학번(stuNum)이 String이므로 String은 compare 메서드가 없는 대신 그와 같은 CompareTo 메서드가 있다.

이것을 사용하여 정렬을 할 수 있다.

따라서 아래와 같은 코딩을 할 수 있다.

내림차순이므로 뒤에 -1을 곱해준다.

총점이 같은 경우가 아니면 총점을 내림차순 정렬하라고 되어있다.

총점은 Integer이기때문에 compareTo메서드가 없는 대신 compare 메서드가 있으므로 이것을 사용하여 정렬할 수 있다.

내림차순 이므로 뒤에 -1을 곱해준다.

Integer에 compareTo를 사용하려고 하면 어느 순간 해결할 수 없는 문제에 직면하게 될것이다.(Ctrl+1로 이클립스에서 추천하는 해결 코드를 적용하다 보면 더이상 수정할 수 없는 순간이 나온다..)

 

📝 결론 

1) 보통 기본적인 비교대상은 Comparable로 한다. (compareTo)

2) compareTo는 String에만 존재한다. (Integer는 compare를 사용해야 한다.)

3) 즉, 기본적인 비교대상이 String이 아닌 int 라면 기본적인 비교대상이 Compatable이 되어야 한다. (compare)

4) Comparable, Compatabl 둘다 int 를 반환하지만 객체 정렬 대상의 타입에 따라 return 방식이 다르다.

 

 

 

💡 Comparable example 추가 설명

내가 구현한 객체 자체에 기본 정렬을 설정하고자 할때 사용함.

Comparable은 객체 간 정렬 시 오름차순, 내림차순 등의 일반적인 순서를 잡는 기준이 필요할 때 객체 클래스에 확장해서 사용한다.

Comparable이 일반적인 순서를 잡는 기준이 된다고 했다.

- 이 Employee객체를 일반적으로 어떻게 정렬할 것인가? => name으로 정렬을 하겠다.

 

- Employee 클래스에 implements로 Comparable을 확장했고

- Comparable에 대한 compareTo()메서드를 오버라이(재정의) 한다.

 

- compareTo()는 int 타입을 반환한다.

그리고 파라미터로 비교 대상이 되는 객체가 들어오게 된다.

 

- int 타입으로 반환하기 때문에 return 하는 int는 기준이 있다. (기준에 맞춰야 함.)

  • compareTo 메서드 작성법
    • 현재 객체 < 파라미터로 넘어온 객체 : 음수 리턴
    • 현재 객체 == 파라미터로 넘어온 객체 : 0 리턴
    • 현재 객체 > 파라미터로 넘어온 객체 : 양수 리턴

💡 Comparator example 추가 설명

기본 정렬외에 다른 정렬을 추가하고 싶은 경우 사용

Comparator는 일반적이지 않은 문자열의 길이 순으로 보고 싶다든지, Comparable로 구현한 것 말고 기준으로 정렬하고 싶을 때 사용한다.

예를 들어 위의 Comparable에서 이름순으로 기본 정렬을 설정했는데 연봉 순으로 재정렬 하거나 부서명으로 재정렬 하고자 할 때 사용한다.

이때, compareTo가 사용중이 아니라면, compareTo를 다시 오버라이딩해서 사용해도되지만

대부분의 경우 사용중이므로,

이럴때 Comparator를 사용하면 기본 정렬 외에 또다른 정렬이 필요한 경우에 특수한 기준을 줄 수 있다.

 

📝 즉 기본 정렬 기준인 comparable에서 설정한 compareTo와 다른 기준으로 정렬을 하고자 할 때 사용한다.

📝 기본적인 정렬 방법인 오름차순 정렬을 내림차순으로 정렬할 때 많이 사용된다.

 

 

아래는 조금 더 자세한 Comparable과 Comparator의 설명이 존재한다.

자세한 설명이 필요한 경우 아래 블로그를 참고한다.

namocom.tistory.com/871

 

[Java] Comparable vs Comparator 비교

Q. 질문 compareble은 정해진 기준으로 정렬할 때 사용하고 comparetor는 사용자가 원하는 기준을 커스텀해서 사용할 때 사용한다고 배웠는데 compareble도 리턴값만 수정하면 사용자가 원하는대로 기준

namocom.tistory.com

 

 

📝 두개의 인터페이스를 구현했다고 해서 정렬이 되는것은 아니다. Collections의 Static 메서드인 sort를 호출하여 정렬한다.

 

 

 

💡 전체 소스코드

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;



public class Student implements Comparable<Student>{
	private String stuNum;//학번
	private String stuName;//이름
	private int kor;//국어점수
	private int eng;//영어점수
	private int mat;//수학점수
	private int subSum;//총점
	private int stuRank;//등수
	
	Student(){
		
	}
	
	Student(String stuNum, String stuName, int kor, int eng, int mat){
		this.stuNum = stuNum;
		this.stuName = stuName;
		this.kor = kor;
		this.eng = eng;
		this.mat = mat; 
		this.subSum = kor + eng + mat;
		this.stuRank = 1;
	}
	
	public String getStuNum() {
		return stuNum;
	}

	public void setStuNum(String stuNum) {
		this.stuNum = stuNum;
	}

	public String getStuName() {
		return stuName;
	}

	public void setStuName(String stuName) {
		this.stuName = stuName;
	}

	public int getKor() {
		return kor;
	}

	public void setKor(int kor) {
		this.kor = kor;
	}

	public int getEng() {
		return eng;
	}

	public void setEng(int eng) {
		this.eng = eng;
	}

	public int getMat() {
		return mat;
	}

	public void setMat(int mat) {
		this.mat = mat;
	}

	public int getSubSum() {
		return subSum;
	}

	public void setSubSum(int subSum) {
		this.subSum = subSum;
	}

	public int getStuRank() {
		return stuRank;
	}

	public void setStuRank(int stuRank) {
		this.stuRank = stuRank;
	}

	@Override
	public int compareTo(Student stu) {
		return this.getStuNum().compareTo(stu.getStuNum());
	}
	
	public static void setRanked(List<Student> students) {
		for(Student st1 : students) {
			int rank = st1.stuRank;
			for(Student st2 : students) {
				if(st1.getSubSum() < st2.getSubSum()) {
					rank++;
				}
			}
			st1.setStuRank(rank);
		}
	}
	
	
	
	
	public static void main(String[] args) {
		List<Student> students = new ArrayList<Student>();
		
		students.add(new Student("201263346", "홍길동", 75,65,52));
		students.add(new Student("201263344", "변학도", 70,60,20));
		students.add(new Student("201263343", "강감찬", 75,65,52));
		students.add(new Student("201263341", "성춘향", 10,95,83));
		students.add(new Student("201263342", "이순신", 75,65,52));
		students.add(new Student("201263345", "일지매", 75,65,52));
		
		setRanked(students);
		
		
		System.out.println("학생목록 정렬 전 : ");
		for(Student stu : students) {
			System.out.println(stu);
		}
		System.out.println("===================================");
		
		Collections.sort(students);
		
		System.out.println("학생목록 정렬 후 : ");
		for(Student stu : students) {
			System.out.println(stu);
		}
		System.out.println("===================================");
		
		System.out.println("총점 같을 때 학번 정렬");
		Collections.sort(students, new SortSubSum());	
		for(Student stu : students) {
			System.out.println(stu);
		}
	}
}

class SortSubSum implements Comparator<Student>{

	@Override
	public int compare(Student stu1, Student stu2) {
		if(stu1.getSubSum() == stu2.getSubSum()) {
			return stu1.getStuNum().compareTo(stu2.getStuNum()) * -1;
		}else {
			return Integer.compare(stu1.getSubSum(), stu2.getSubSum()) * -1;
		}
	}
	
}

 

'BackEnd > Java' 카테고리의 다른 글

JDK 버전 별 컴파일러 지원기능 정리  (0) 2021.03.05
Set & TreeSet  (0) 2021.03.05
Java equals(), HashCode()  (2) 2021.03.04
MVC 패턴  (0) 2021.03.04
Maven - Oracle 설정 (pom.xml)  (0) 2021.03.04