Comparable VS Comparator

Mar 11, 2018    

Comparable VS Comparator

Comparable

Comparable 인터페이스는 상속받는 클래스가 생성한 객체 사이의 자연스러운 정렬 이 가능하도록 도입되었다. 인터페이스를 상속받은 클래스는 compareTo 메서드를 구현해야 하며 이는 객체 사이의 비교를 담당하며 Arrays.sortCollections.sort 같은 정렬 API에서 사용한다.

아래와 같은 클래스에서 인터페이스를 상속받고 메서드를 구현했다. 이름의 오름차순으로 구현했으며, 이는 다음 테스트 코드와 같이 동작한다.

// Comparable 구현
class Fruit implements Comparable<Fruit> {
	private String name;
	private int price;
	Fruit(String name, int price) {
		this.name = name;
		this.price = price;
	}
	@Override
	public int compareTo(Fruit o) {
		Fruit f = (Fruit) o;
		return this.name.compareTo(f.name);
	}
	public String getName() {
		return name;
	}
	public int getPrice() {
		return price;
	}
}
// Comparable의 동작
public class CompareTest {
	@Test
	public void ObjectComparableTest() {
		Fruit[] f = new Fruit[4];
		f[0] = new Fruit("Orange", 10);
		f[1] = new Fruit("Mango", 70);
		f[2] = new Fruit("Melon", 50);
		f[3] = new Fruit("Apple", 20);

		String[] alphabet = {"Apple", "Mango", "Melon", "Orange"};
		/**
		 * Fruit 클래스는 기본적으로 이름을 기준으로 비교한다.
		 */
		Arrays.sort(f);
		for(int i = 0; i < 4; i++)
			assertTrue(f[i].getName().equals(alphabet[i]));
	}
}

Comparator

하지만 어떤 동작에선 이름이 아니라 가격으로 비교해야 할 경우가 있다고 가정하자. 그리고 이름으로 정렬하는 기능도 동시에 사용해야 한다. 이럴 땐 Comparator를 사용해서 상황별 정렬 기준 변경이 가능하다.

public class CompareTest {
	@Test
	public void ObjectComparatorTest() {
		Fruit[] f = new Fruit[4];
		f[0] = new Fruit("Orange", 10);
		f[1] = new Fruit("Mango", 70);
		f[2] = new Fruit("Melon", 50);
		f[3] = new Fruit("Apple", 20);

		String[] cheap = {"Orange", "Apple", "Melon", "Mango"};

		/**
		 * 이름 대신 가격으로 비교하고 싶다
		 * 그럼 비교할 때마다 비교로직을 변경해야 하는가?
		 * 이름과 가격 둘 다 비교해야 하는 경우는 어떻게 해야하나?
		 */
		Arrays.sort(f, new Comparator<Fruit>() {

			@Override
			public int compare(Fruit o1, Fruit o2) {
				// 오름차순
				return o1.getPrice() - o2.getPrice();
			}

		});

		for(int i = 0; i < 4; i++)
			assertTrue(f[i].getName().equals(cheap[i]));
	}

	@Test
	public void ObjectComparatorDescendingTest() {
		Fruit[] f = new Fruit[4];
		f[0] = new Fruit("Orange", 10);
		f[1] = new Fruit("Mango", 70);
		f[2] = new Fruit("Melon", 50);
		f[3] = new Fruit("Apple", 20);

		String[] expensive = {"Mango", "Melon", "Apple", "Orange"};

		Arrays.sort(f, new Comparator<Fruit>() {

			@Override
			public int compare(Fruit o1, Fruit o2) {
				return o2.getPrice() - o1.getPrice();
			}

		});

		for(int i = 0; i < 4; i++)
			assertTrue(f[i].getName().equals(expensive[i]));
	}
}

위 테스트 코드는 이미 Comparable로 구현된 Fruit 객체를 정렬해야하는 상황 별로 price 오름차순, 내림차순으로 정렬하고 있다. 이는 클래스의 비교로직에 영향을 끼치지 않으며 익명 객체가 사용된 코드에만 영향을 미친다.

Java의 Comparable, Comparator