[Java] Comparable / Comparator

현굥·2024년 10월 6일

Java

목록 보기
8/8

사용 의의

객체를 비교할 수 있도록 만들어줍니다.

기본 자료형(primitive types)은 자바에서 내장된 부등호(>, <)로 비교가 가능합니다.

하지만, 사용자가 정의한 클래스의 객체를 비교하려면 명확한 기준을 지정해야 합니다.

예를 들어, 학생 객체 두 개를 비교할 때, 성적, 나이, 이름 등 다양한 기준을 정할 수 있습니다.

특정 기준에 따른 객체간의 비교를 위해 Comparable 또는 Comparator 인터페이스를 사용합니다.

Comparable vs Comparator

이 둘은 모두 인터페이스입니다.

Comparable 와 Comparator를 사용해주려면 인터페이스 내에 선언된 메소드를 반드시 오버라이딩해줘야 합니다.

Comparable 인터페이스는 compareTo(T o) 메소드를 오버라이딩
Comparator 인터페이스는 compare(T o1, T o2) 메소드를 오버라이딩

Comparable 인터페이스는 기본적으로 '자기 자신'과 비교하는 메소드인 compareTo를 오버라이딩하여 사용합니다.

Comparator 인터페이스는 '두 객체'를 비교하는 메소드인 compare를 오버라이딩하여 사용합니다.


Comparable 의 특징

  • compareTo(T o) 메소드를 재정의해야 합니다.
  • 자기 자신과 매개변수로 전달된 객체를 비교하는 방식입니다.

클래스에 Comparable 인터페이스를 implements하여 사용하며, compareTo() 메소드를 오버라이딩하여 비교 기준을 정의합니다.

기본 틀


class ClassName implements Comparable<ClassName> {

    @Override
    public int compareTo(ClassName o) {
    
    
        // 비교 로직
    }
}

비교구현

compareTo() 메소드는 자기 자신과 매개변수 객체의 특정 속성을 비교하여, 그 차이에 따라 양수, 0, 음수로 값을 반환하는데, 각각의 경우를 1, 0, -1로 표현하는게 일반적입니다.

이 코드는 두 객체의 나이를 비교하여, 나이가 많으면 양수(1), 같으면 0, 적으면 음수(-1)를 반환합니다.

class Student implements Comparable<Student> {
 
	int age;			// 나이
	int classNumber;	// 학급
	
	Student(int age, int classNumber) {
		this.age = age;
		this.classNumber = classNumber;
	}
	
	@Override
	public int compareTo(Student o) {
    
		// 자기자신의 age가 o의 age보다 크다면 양수
		if(this.age > o.age) {
			return 1;
		}
		// 자기 자신의 age와 o의 age가 같다면 0
		else if(this.age == o.age) {
			return 0;
		}
		// 자기 자신의 age가 o의 age보다 작다면 음수
		else {
			return -1;
		}
	}
}

위의 방법을 더 간편하게 바꾸어 두 값의 차이를 return 해주어도 되므로, 아래의 코드처럼 두 값의 차를 return해도 결과는 똑같습니다.

(단, 빼는 과정에서 Overflow가 발생할수도 있으므로, 범위에 주의해야됨)

class Student implements Comparable<Student> {
 
	int age;			// 나이
	int classNumber;	// 학급
	
	Student(int age, int classNumber) {
		this.age = age;
		this.classNumber = classNumber;
	}
	
	@Override
	public int compareTo(Student o) {
 
		return this.age - o.age;
	}
}

Comparator 의 특징

  • compare(T o1, T o2) 메소드를 재정의합니다.
  • 두 개의 객체를 비교할 수 있습니다.

Comparator는 별도의 비교 로직을 외부에서 정의할 수 있기 때문에, 하나의 클래스에 여러 기준으로 정렬을 적용할 수 있습니다.

기본 틀


import java.util.Comparator;

class ClassName implements Comparator<ClassName> {
    @Override
    public int compare(ClassName o1, ClassName o2) {
        // 비교 로직
    }
}

비교 구현

compare() 메소드는 두 객체 간의 특정 속성을 비교하고, 차이에 따라 양수, 0, 음수를 반환합니다.

아래의 코드는 두 객체의 나이를 비교하여, 나이가 많으면 양수(1), 같으면 0, 적으면 음수(-1)를 반환합니다.

import java.util.Comparator;	// import 필요
class Student implements Comparator<Student> {
 
	int age;			// 나이
	int classNumber;	// 학급
	
	Student(int age, int classNumber) {
		this.age = age;
		this.classNumber = classNumber;
	}
	
	@Override
	public int compare(Student o1, Student o2) {
    
		// o1의 학급이 o2의 학급보다 크다면 양수
		if(o1.classNumber > o2.classNumber) {
			return 1;
		}
		// o1의 학급이 o2의 학급과 같다면 0
		else if(o1.classNumber == o2.classNumber) {
			return 0;
		}
		// o1의 학급이 o2의 학급보다 작다면 음수
		else {
			return -1;
		}
	}
}

위의 방법을 더 간편하게 바꾸어 두 값의 차이를 return 해주어도 되므로, 아래의 코드처럼 두 값의 차를 return해도 결과는 똑같습니다.

(마찬가지로, 빼는 과정에서 Overflow가 발생할수도 있으므로, 범위에 주의해야됨)

import java.util.Comparator;	// import 필요
class Student implements Comparator<Student> {
 
	int age;			// 나이
	int classNumber;	// 학급
	
	Student(int age, int classNumber) {
		this.age = age;
		this.classNumber = classNumber;
	}
	
	@Override
	public int compare(Student o1, Student o2) {
 
		/*
		 * 만약 o1의 classNumber가 o2의 classNumber보다 크다면 양수가 반환 될 것이고,
		 * 같다면 0을, 작다면 음수를 반환할 것이다.
		 */
		return o1.classNumber - o2.classNumber;
	}
}

Comparator 활용편 (익명 객체 사용)

Comparator를 구현하는 익명객체를 생성하여 이용하려는 클래스 내부에서 구현해주지 않고도 사용해줄 수 있습니다.

익명 객체는 main() 메서드 안에서 non-static으로 생성해도 되고, 필요에 따라 static으로 선언해 사용할 수도 있습니다.

익명 객체는 필요에 따라 여러 개를 생성할 수 있고 변수명만 달리하면 다양한 비교 기준을 정의할 수 있습니다.

아래의 코드는 학급, 나이를 썼는데 성적 등등 원하는대로 더 추가할 수 있습니다.

Comparator _ 익명객체 구현 예시


import java.util.Comparator;
 
public class Test {
	public static void main(String[] args)  {
 
		Student a = new Student(17, 2);	// 17살 2반
		Student b = new Student(18, 1);	// 18살 1반
		Student c = new Student(15, 3);	// 15살 3반
			
		// 학급 기준 익명객체를 통해 b와 c객체를 비교한다.
		int classBig = comp.compare(b, c);
		
		if(classBig > 0) {
			System.out.println("b객체가 c객체보다 큽니다.");
		}
		else if(classBig == 0) {
			System.out.println("두 객체의 크기가 같습니다.");
		}
		else {
			System.out.println("b객체가 c객체보다 작습니다.");
		}
		
		// 나이 기준 익명객체를 통해 b와 c객체를 비교한다.
		int ageBig = comp2.compare(b, c);
		
		if(ageBig > 0) {
			System.out.println("b객체가 c객체보다 큽니다.");
		}
		else if(ageBig == 0) {
			System.out.println("두 객체의 크기가 같습니다.");
		}
		else {
			System.out.println("b객체가 c객체보다 작습니다.");
		}
		
	}
	
	// 학급 대소 비교 익명 객체
	public static Comparator<Student> comp = new Comparator<Student>() {
		@Override
		public int compare(Student o1, Student o2) {
			return o1.classNumber - o2.classNumber;
		}
	};
	
	// 나이 대소 비교 익명 객체
	public static Comparator<Student> comp2 = new Comparator<Student>() {
		@Override
		public int compare(Student o1, Student o2) {
			return o1.age - o2.age;
		}
	};
}
 
class Student {
 
	int age;			// 나이
	int classNumber;	// 학급
	
	Student(int age, int classNumber) {
		this.age = age;
		this.classNumber = classNumber;
	}
	
}

Arrays.sort(), Collections.sort() 정렬 기준 설정

자바에서 배열이나 리스트를 정렬할 때, Arrays.sort()Collections.sort() 메소드를 사용하게 되는데, 숫자나 문자열 등 primitive type에서만 동작하고, 오름차순으로 정렬됩니다.

이를 확장시켜, Arrays.sort()Collections.sort() 메소드를 이용하여 객체배열이나 리스트를 ComparatorComparable 인터페이스를 통해 비교 기준을 정의하여 정렬할 수 있습니다.

오름차순

public int compare(MyClass o1, MyClass o2) {
    return o1.value - o2.value;  // 오름차순: 작은 값이 앞에 오도록
}

내림차순

public int compare(MyClass o1, MyClass o2) {
    return o2.value - o1.value;  // 내림차순: 큰 값이 앞에 오도록
}

예시 코드 _Comparator를 사용한 정렬

import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student(17, 2));  // 17살 2반
        students.add(new Student(18, 1));  // 18살 1반
        students.add(new Student(15, 3));  // 15살 3반

        // 익명 객체를 사용한 Comparator 구현
        Collections.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.age - o2.age;  // 나이 기준 정렬
            }
        });

        // 정렬 결과 출력
        for (Student s : students) {
            System.out.println("나이: " + s.age + ", 학급: " + s.classNumber);
        }
    }
}

class Student {
    int age;
    int classNumber;

    Student(int age, int classNumber) {
        this.age = age;
        this.classNumber = classNumber;
    }
}

예시code2

import java.util.*;

class Student {
    int age;
    int score;

    Student(int age, int score) {
        this.age = age;
        this.score = score;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student(20, 85));
        students.add(new Student(18, 90));
        students.add(new Student(22, 70));

        // 성적 기준으로 정렬
        Collections.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o2.score - o1.score;  // 성적 내림차순 정렬
            }
        });

        for (Student s : students) {
            System.out.println("Age: " + s.age + ", Score: " + s.score);
        }
    }
}

참고블로그

0개의 댓글