[Java] Comparable과 Comparator_배열 정렬 구체화

JTI·2022년 11월 22일
0

☕️  Java

목록 보기
27/59
post-thumbnail

📌 들어가기 전


객체 배열의 정렬하는 기준은 2가지로 나눠볼 수 있다.

1. Comparable<T>
2. Comparator<T>

✏️ 핸드폰 객체 배열

import java.util.Arrays;
class Phone {
	String model;
	int price;

	public Phone(String model, int price) {
		this.model = model;
		this.price = price;
	}
	@Override
	public String toString() {
		return model + "(" + price + ")";
	}
}

class SortEx2 {
	public static void main(String[] args) {
		Phone[] list = {
			new Phone("갤럭시20plus", 900000),
			new Phone("갤럭시22ultra", 130000),
			new Phone("아이폰14proMax", 1800000),
			new Phone("아이폰13plus", 1100000),
			new Phone("갤럭시z플립4", 1350000)
		};
		Arrays.sort(list); // 여기서 문제가 생겼음(형변환 하고 있다.)
	}
}
Exception in thread "main" java.lang.ClassCastException: Phone cannot be cast to java.lang.Comparable
        at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
        at java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
        at java.util.Arrays.sort(Arrays.java:1246)
        at SortEx2.main(SortEx2.java:25)

폰 타입은 Comparable 로 형변환이 안된다는 에러문구이다.

그말은, sort() 메서드에서 Comparable 형변환 하고 있다는 뜻이다.
정렬의 기준이 어떤 것인지 잡아주지 않았기 때문에 이런 에러문구가 떴다.

어떤게 크고 어떤게 같고 어떤게 작다는 것을 compareTo() 에 오버라이드 해야 한다.

❗️ Phone이 comareTo() 를 implements 하면된다.

그럼 여기서, Comparable 이 무엇일까?

💡 Comparable<T> 인터페이스


✔️ 객체를 정렬하는 데 사용되는 메소드인 compareTo() 메서드를 정의

자바에서 같은 타입의 인스턴스를 서로 비교해야만 하는 클래스들은 모두 Comparable 인터페이스를 구현하고 있다.

따라서 Boolean을 제외한 래퍼 클래스나 String, Time, Date와 같은 클래스의 인스턴스는 모두 정렬 가능하다.

이때 기본 정렬 순서는 작은 값에서 큰 값으로 정렬되는 오름차순이 된다.

📎 사용 방법

✔️ compareTo(T o)

  • 비교하는데 이 메서드의 소유자(this object)와 파라미터의 o를 비교한다.
  • 리턴값은 음수, 양수, 0이다.
  • this < o : 음수
  • this > o : 양수
  • this = o : 0

어떤 순서로 비교를 할 것이고 어떻게 정렬할 것인지는 sort() 가 할 일이고
우리가 해 줘야하는 것은 어떨 때 작고 같고 큰지를 알려줘야 한다.
그 기능이 comparTo 이다.

📎 Comparable의 특징

  • 클래스가 상속받아서 compareTo 메서드를 구현함
  • 해당 객체 배열은 기본적으로 compareTo 구현방식대로 정렬됨.
  • 모든 값 클래스, 열거 타입은 Comparable이 구현되어져 있으므로 sort() 사용이 가능한 것.
  • 정렬기준이 하나밖에 없어서 정렬기준을 바꾸는게 어렵다.
  • Collections.reverseOrder() 사용이 가능하다.

✏️ Comparable<T> 적용

그럼 다시 폰객체로 돌아가서 적용해 보자!

import java.util.Arrays;

class Phone implements Comparable<Phone> { // 폰이라는 클래스는 전화기끼리 비교가 가능한 클래스가 된 것.
	String model;
	int price;

	public Phone(String model, int price) {
		this.model = model;
		this.price = price;
	}
	@Override
	public String toString() {
		return model + "(" + price + ")";
	}
	/*
		a.compareTo(b)
		a > b: 양수
		a < b: 음수
		a = b: 0 -> 정렬못함.
	*/
	@Override
	public int compareTo(Phone other) {
		int otherPrice = other.price;
		if(price > otherPrice) {
			return 1;
		} else if(price < otherPrice) {
			return -1;
		} else {
			return 0;
		}
	}
}

class SortEx2 {
	public static void main(String[] args) {
		Phone[] list = {
			new Phone("갤럭시20plus", 900000), // a
			new Phone("갤럭시22ultra", 1300000), // b
			new Phone("아이폰14proMax", 1800000),
			new Phone("아이폰13plus", 1100000),
			new Phone("갤럭시z플립4", 1350000)
		};
		Arrays.sort(list);
		System.out.println(Arrays.toString(list));
	}
}

이렇게 compareTo 를 오버라이딩 받아서 정렬기준을 세워줘야 한다.

❗️ 간단 조건

@Override
public int compareTo(Phone other) {
	return price - other.price;
}

❗️ 내림차순

@Override
public int compareTo(Phone other) {
	return (price - other.price) * -1;
}

🤔 만약 가격이 같은게 있다면 ❓

차선을 더 적어줘야한다.
그러면 가격이 같으면 이름으로 오름차순하는 걸로 바꿔보자.

@Override
public int compareTo(Phone other) {
	int result = price - other.price;
	if(result == 0) {
		//두 전화기의 가격이 같은경우, 이름으로 오름차순 정렬한다.
		result = model.compareTo(other.model);
	}
	return result;
}
[결과값]
[갤럭시20plus(900000), 갤럭시20ultra(1100000), 아이폰12proMax(1100000), 아이폰13plus(1100000), 갤럭시22ultra(1300000),  갤럭시z플립4(1350000), 아이폰14proMax(1800000)]

이렇게 같은 가격인데도 이름으로 오름차순 된 것을 확인할 수 있다.

💡 Comparator<T> 인터페이스


✔️ 객체를 정렬하는 데 사용되는 메소드인 compare() 메서드를 정의

Comparator 인터페이스는 Comparable 인터페이스와 같이 객체를 정렬하는 데 사용되는 인터페이스이다.

Comparable 인터페이스를 구현한 클래스는 기본적으로 오름차순으로 정렬되지만, 내림차순이나 아니면 다른 기준으로 정렬하고 싶을 때 사용할 수 있다.

📎 사용방법

✔️ compare(T o1, T o2)

  • 전달된 두 객체의 순서를 비교한다.
  • 첫번째 인자가 기준이 되며, 리턴값은 음수, 양수, 0이다.
  • o1 < o2 : 음수
  • o1 > o2 : 양수
  • o1 = o2 : 0

✔️ boolean equals(Object obj)

  • 해당 comparator와 객체가 같은지 확인함.

✔️ default Comparator<T>reversed()

  • 해당 comparator의 역순인 comparator를 반환함.

✔️ public static void sort (T[] a, Comparator<? super T> c)
❗️ T[]배열로 Comparator 정렬하겠다는 뜻.

T의 상위타입의 뭔가를 비교할 수 있는 Comparator여야 한다.
제네릭안에 들어가야하는 것은 원소의 상위타입이여야 한다는 것이다.

<? super T>: T의 상위 타입 (하향제한)
<? extends T>: T의 하위 타입 (상향제한)
<?>: Object

그냥 위에꺼 신경안쓰고 간단하게 T라고 생각하면 된다.

📎 Comparator의 특징

  • sort마다 원하는 방식으로 자유롭게 정렬하기 좋다.
  • 구현체를 선언해두고 재사용도 할 수 있다.
  • sort에서 람다식으로 비교적 짧게 축약사용이 가능하다.

✏️ Comparator<T> 적용

import java.util.Arrays;
import java.util.Comparator;
class Phone {
    String model;
    int price;

    public Phone(String model, int price) {
        this.model = model;
        this.price = price;
    }
    @Override
    public String toString() {
        return model + "(" + price + ")";
    }
}
//가격 오름차순 정렬
class OrderByPrice implements Comparator<Phone>{
    @Override
    public int compare(Phone o1, Phone o2) {
        int result = o1.price - o2.price;
        if(result == 0) {
            result = (o1.model.compareTo(o2.model)) * -1;
        }
        return result;
    }
}
//모델 오름차순 정렬
class OrderByModel implements Comparator<Phone> {
    @Override
    public int compare(Phone o1, Phone o2) {
        int result = o1.model.compareTo(o2.model);
        if(result == 0) {
            result = o1.price - o2.price;
        }
        return result;
    }
}


public class PhoneTest {
    public static void main(String[] args) {
        Phone[] phones = {
                new Phone("갤럭시20plus", 900000),
                new Phone("갤럭시22ultra", 1300000),
                new Phone("아이폰14proMax", 1800000),
                new Phone("아이폰13plus", 1100000),
                new Phone("갤럭시z플립4", 1300000)

        };
        Arrays.sort(phones, new OrderByPrice());
        System.out.println(Arrays.toString(phones));
    }
}
[결과값]
[갤럭시20plus(900000), 아이폰13plus(1100000), 갤럭시z플립4(1300000), 갤럭시22ultra(1300000), 아이폰14proMax(1800000)]
profile
Fill in my own colorful colors🎨

0개의 댓글