Comparable / Comparator

류기탁·2021년 12월 13일
0

CodingTest/Algorithm

목록 보기
13/22

1. 개요

가. 공통적으로 하는 일

  • 객체를 비교할 수 있도록 만든다.
  • Comparator과 Comparable은 모두 인터페이스로 컬렉션을 정렬하는데 필요한 메소드를 정의하고 있으며, 둘 다 객체의 정렬을 위해서 사용한다.
  • 기본 자료형인 primitive type은 자바 자체에서 제공하기에 별다른 처리가 필요 없지만, 새로운 클래스를 만들어 비교할 때는 이것 들이 필요하다.

    학생 클래스를 만들 때, 나이를 기준으로 비교할 것인지 학급을 기준으로 비교할 것인지 설정하는 것

나. Comparable/Comparator 란?

  • Comparable, Comparator 모두 인터페이스(interface)이다.
  • 사용하고자 한다면 인터페이스 내에 선언된 메소드를 반드시 구현해야한다는 것이다.

Comparable Interface

  • 자기 자신과 매개변수 객체를 비교
  • 자기 자신과 파리미터로 들어오는 객체를 비교한다.
  • compareTo(T o) 메소드를 재정의(오버라이딩)해주어야 한다.
  • lang 패키지에 들어있어서 import가 따로 필요 없다.

Comparator Interface

  • 두 매개 변수 객체를 비교
  • compare(T o1, T o2) 메소드를 재정의(오버라이딩)해주어야 한다. - 파라미터로 들어오는 두 객체를 비교한다.
  • util 패키지에 있어서 import를 해주어야한다.

2. Comparable - compareTo()

가. 개념

  • 보통 기본 정렬기준을 구현하는데 사용한다.
  • compareTo메소드는 int값을 반환하도록 되어 있다.
  • 값을 비교해서 정수를 반환한다는 의미이다.
  • 자기자신을 기준을 삼아서, 대소관계를 파악해야한다.
    • 자기자신이 상대방 보다 크면 -> 양수를 반환한다.
    • 자기자신과 상대방이 같으면 -> 0을 반환한다.
    • 자기자신이 상대방보다 작으면 ->
      음수를 반환한다.

나. Comparable 사용법 (java)

public interface Comparable {
	int compareTo(Object o); 
	//객체 자신(this)와 o를 비교한다.
}

예시

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 142352;
		}
		// 자기 자신의 age와 o의 age가 같다면 0
		else if(this.age == o.age) {
			return 0;
		}
		// 자기 자신의 age가 o의 age보다 작다면 음수
		else {
			return -1324;
		}
	}
}

양수 음수 값은 관계가 없기 때문에, 그냥 차이를 반환해도 된다.

@Override
	public int compareTo(Student o) {
		/*
		 * 만약 자신의 age가 o의 age보다 크다면 양수가 반환 될 것이고,
		 * 같다면 0을, 작다면 음수를 반환할 것이다.
		 */
		return this.age - o.age;
	}
  • 주의 할 점
    범위를 초과하지 않는지 주의 하자. (언더플로, 오버플로)

3. Comparator - compare()

가. 개념

  • 파라미터로 들어오는 두 객체를 비교한다.
  • 보통 기본 정렬기준 외에 다른 기준으로 정렬하고자 할때 사용한다.

나. Comparator 사용법 (java)

public interface Comparator {
	int compare(Object o1, Object o2);
	boolean equals(Object obj);
}

구현

import java.util.Comparator;	// import 필요
// Comparator<Student> 를 implement 한다.
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;
		}
	}
}

Comparator을 통해 , compare 메소드를 사용하려면, compare 메소드를 활용하기위한 객체가 필요하다.
하지만 compare을 사용하기 위해선 쓸모가 없음에도 새로운 변수를 만들거나, 일관성이 떨어지는 코드를 작성해야한다는 문제가 발생한다. 그럴 땐 익명 객체를 사용하기로 한다.

3.익명 객체 (anonymous)

Comparator의 비교 기능만 따로 사용하고싶다.

가. 개요

  • 익명 객체란, 이름이 정의되지 않은 객체를 의미한다.
  • 어떤 객체를 만들 때는 class를 생성하여 이름을 정의한다. 이름없이 클래스를 정의하려고 할 때 사용한다.
  • 또는, 부분적으로 기능을 일시적으로 바꾸거나, 특정 구현 부분만 따로 사용한다던가 할 때 쓸 수 있는 것이 익명객체이다.
  • 클래스의 선언과 객체생성을 동시에 하기 때문에 단 한번만 사용할 수 있고, 오직 하나의 객체만을 생성하는 일회용 클래스이다.

나. 익명 객체 사용법(java)

class name = new class() { ~~~ }; 로 표현한다.

public class Test{
    private String st = "test";
    public String getSt() {
        return this.st;
    }
}
public static void main(String[] args) {
    // 일반적인 사용
    Test ABC = new Test();
    System.out.println(ABC.getSt());

    // 익명 객체를 사용
    Test DEF = new Test() {
        private String st = "유후";
        @Override
        public String getSt(){
            return this.st;
        }
    } 
    System.out.println(DEF.getSt());
    // 유후가 출력된다.
}

Comparator의 익명객체

그렇다면, Comparator의 기능만 사용하고 싶을 때, Comparator는 interface이고, 이를 구현하는 익명객체를 생성하면 된다.

  • 익명 객체를 가리키는 변수명만 다르게 하면 여러개를 생성할 수 있다.

    학급을 기준으로 대소비교를 하거나, 나이를 기준으로 대소비교를 할 수 있다.

import java.util.Comparator;


public class Test {
	public static void main(String[] args) {
    
		// 익명 객체 구현방법 1
		Comparator<Student> comp1 = new Comparator<Student>() {
			@Override
			public int compare(Student o1, Student o2) {
				return o1.classNumber - o2.classNumber;
			}
		};
	}
 
	// 익명 객체 구현 2
	public static Comparator<Student> comp2 = new Comparator<Student>() {
		@Override
		public int compare(Student o1, Student o2) {
			return o1.classNumber - o2.classNumber;
		}
	};
}

// 외부에서 익명 객체로 Comparator가 생성되기 때문에 클래스에서 Comparator을 구현 할 필요가 없어진다.
class Student {
 
	int age;			// 나이
	int classNumber;	// 학급
	
	Student(int age, int classNumber) {
		this.age = age;
		this.classNumber = classNumber;
	}
 
}

4. Java에서의 정렬 (SORT)

가. 개요

  • Java에서는, 일반적으로 오름차순을 기준으로 정렬한다.
  • Arrays.sort() / Collections.sort()가 대표적인 예시이다.
  • 선행원소가, 후행 원소보다 작을 경우. 앞선 메소드에서 리턴값은 음수 이다.
  • 배열이나 리스트를 정렬하면서 index1과 index2의 원소를 비교하여 정렬하는 방법은 Comparator의 메소드를 사용하여 양수가 나오면 두 원소의 위치를 바꾼다.라고 할 수 있다. (앞의 원소의 크기가 크기 때문에)

나. 정렬 사용법(java)

다음과 같은 클래스가 있다.

public class Test {	
	public static void main(String[] args) {
		MyInteger[] arr = new MyInteger[10];
		// 객체 배열 초기화 (랜덤 값으로) 
		for(int i = 0; i < 10; i++) {
			arr[i] = new MyInteger((int)(Math.random() * 100));
		}
	}
	
}
 
class MyInteger {
	int value;
	public MyInteger(int value) {
		this.value = value;
	}
}

MyInteger 클래스의 비교기준은 없다.

  • Comparable을 이용해서 구현한다.
class MyInteger implements Comparable<MyInteger> {
	int value;
	public MyInteger(int value) {
		this.value = value;
	} 
	// 자기 자신의 value을 기준으로 파라미터 값과의 차이를 반환한다.
	@Override
	public int compareTo(MyInteger o) {
		return this.value - o.value;
	}
	
}

import java.util.Arrays;
public class Test {
	public static void main(String[] args) {
		MyInteger[] arr = new MyInteger[10];
		// 객체 배열 초기화 (랜덤 값으로) 
		for(int i = 0; i < 10; i++) {
			arr[i] = new MyInteger((int)(Math.random() * 100));
		}
 
		// 정렬 이전
		System.out.print("정렬 전 : ");
		for(int i = 0; i < 10; i++) {
			System.out.print(arr[i].value + " ");
		}
		System.out.println();
		
		Arrays.sort(arr);
        
		// 정렬 이후
		System.out.print("정렬 후 : ");
		for(int i = 0; i < 10; i++) {
			System.out.print(arr[i].value + " ");
		}
		System.out.println();
	}
	
}
  • Comparator을 이용하여 구현한다.
import java.util.Arrays;
import java.util.Comparator;
public class Test {
	public static void main(String[] args) {
		MyInteger[] arr = new MyInteger[10];
		// 객체 배열 초기화 (랜덤 값으로) 
		for(int i = 0; i < 10; i++) {
			arr[i] = new MyInteger((int)(Math.random() * 100));
		}
 
		// 정렬 이전
		System.out.print("정렬 전 : ");
		for(int i = 0; i < 10; i++) {
			System.out.print(arr[i].value + " ");
		}
		System.out.println();
		
		Arrays.sort(arr, comp);		
		// MyInteger에 대한 Comparator을 구현한 익명객체를 넘겨줌
        
		// 정렬 이후
		System.out.print("정렬 후 : ");
		for(int i = 0; i < 10; i++) {
			System.out.print(arr[i].value + " ");
		}
		System.out.println();
	}
 
	
	static Comparator<MyInteger> comp = new Comparator<MyInteger>() {
		@Override
		public int compare(MyInteger o1, MyInteger o2) {
			return o1.value - o2.value;
		}
	};
}
 
 
class MyInteger {
	int value;
	public MyInteger(int value) {
		this.value = value;
	}
	
	
}

Comparator 예제 2

class Main {
	public static void main(String[] args){
		Integer[] arr = {30,50,10,40,20};
		//일반 디폴트 정렬
		// Integer가 가지고 있는 기본 정렬 compareTo로 정렬함
		Arrays.sort(arr);
		System.out.println(Arrays.toString(arr));

		// 만들어서 구현한 정렬 기준으로 
		Arrays.sort(arr, new XXXX());
		Arrays.sort(arr, new XXXX());
		/// XXXX라는 정렬 기준을 사용한다.
		System.out.println(Arrays.toString(arr));
	}
}

class XXXX implements Comparator {

}
class XXXX implements Comparator {
	public int compare(Object o1, Object o2 ){
		Integer A = (Integer) o1;
		Integer B = (Integer) o2;
		return A.compareTo(B) * -1;
		return B - A; // 이렇게 두가지 방식이 가능하다.
	}
}

내림차순의 경우

반대로 값을 리턴해주면 된다.

// Comparable
public int compareTo(MyClass o) {
	return -(this.value - o.value);
}
 
// Comparator
public int compare(Myclass o1, MyClass o2) {
	return -(o1.value - o2.value);
}

또는

// Comparable
public int compareTo(MyClass o) {
	return o.value - this.value;	// == -(this.value - o.value);
}
 
// Comparator
public int compare(Myclass o1, MyClass o2) {
	return o2.value - o1.value;		// == -(o1.value - o2.value);
}
참고
profile
오늘도 행복한 하루!

0개의 댓글