개발/Java

[Java] 자바 객체 배열, 리스트 정렬하기 - Comparable vs. Comparator

선우. 2024. 5. 19. 23:35

 🚩목차

    개요

     자바 객체 배열 또는 리스트를 정렬하는 세가지 방법을 알아보겠습니다. 예제에서는 int형 멤버변수 x, y를 갖는 Pair 클래스를 예로 들겠습니다. Pair의 정렬 기준으로 a가 작은 객체가 먼저, a가 같다면 b가 작은 객체가 먼저 오도록 하겠습니다.

    public class Pair {
        int x;
        int y;
        
        public Pair(int x, int y) {
        	this.x = x;
            this.y = y;
        }
    }

     

    Comparable 인터페이스 구현

     compareTo 메서드는 this 객체와 other(비교 대상) 객체 중 this가 other 보다 먼저와야 한다면 -1을, this가 other 보다 나중에 와야 한다면 1을, 정렬 우위가 동일하다면 0을 반환하도록 구현하면 됩니다.

    public class Pair implements Comparable<Pair> {
        int x;
        int y;
        
        public Pair(int x, int y) {
        	this.x = x;
            this.y = y;
        }
        
        @Override
        public int compareTo(Pair other) {
            if(this.x != other.x) {
            	return Integer.compare(this.x, other.x);
            } else {
            	return Integer.compare(this.y, other.y);
            }
        }
    }
    • this와 other 객체 간 비교를 통해 this 객체의 x값이 더 작을 경우 -1을, 더 클 경우 1을 반환합니다.
    • this와 other의 x값이 동일할 경우, y값 비교를 통해 this의 y가 더 작을 경우 -1을, 클 경우 1을 반환합니다.

     하지만 이 방법은 애플리케이션 실행 중 Pair 객체의 정렬 기준을 변경할 수 없습니다. 정렬할 때 마다 정렬 기준을 다르게 적용하려면 Comparator 함수형 인터페이스를 구현하면 됩니다.

     

    Comparator 함수형 인터페이스 구현

    sort 메서드를 호출할 때 정렬 기준으로 넣어주는 Comparator를 익명 클래스로 구현할 수 있습니다.

    public class Solution{
        public static void main(String args[]) throws Exception {
            List<Pair> immutableList = List.of(new Pair(3,1), new Pair(2,5), new Pair(2,3));
            List<Pair> list = new ArrayList<>(immutableList);
            
            list.sort(new Comparator<Pair>() {
                @Override
                public int compare(Pair p1, Pair p2) {
                    if(p1.x != p2.x) {
                        return Integer.compare(p1.x, p2.x);
                    } else {
                        return Integer.compare(p1.y, p2.y);
                    }
                }
            });
            
            for(Pair p : list) {
                System.out.println(p.x + " " + p.y);
            }
        }
    }

     

     Comparator 함수형 인터페이스를 익명 클래스로 구현하는 방법은 정렬할 때 마다 그 기준을 다르게 줄 수 있다는 장점이 있습니다. 하지만 다소 복잡해 가독성이 떨어집니다. Comparator의 comparing을 사용하면 깔끔하게 정리할 수 있습니다.

     

    Comparator comparing 사용

    comparing 내부 구현

     Comparator 인터페이스의 comparing~ 종류의 메서드들은 Comparator를 반환합니다. 따라서 sort 메서드의 Comparator 매개변수에 생성한 Comparator를 대입하면 됩니다.

     

     comparing 메서드는 매개변수로 받은 Function타입 함수형 인터페이스 keyExtractor의 apply 메서드를 호출합니다. 그리고 apply 메서드의 반환 값 끼리 비교하는 Comparator를 반환합니다.

     

    즉, comparing 메서드 매개변수에 객체의 비교 기준이 될 멤버 변수를 반환하는 람다식을 작성하면 됩니다.

    public class Solution{
    	public static void main(String args[]) throws Exception {
    		List<Pair> imlist = List.of(new Pair(3,1), new Pair(2,5), new Pair(2,3));
    		List<Pair> list = new ArrayList<>(imlist);
    		
    		Comparator<Pair> comparator = Comparator
    				.comparing((Pair p) -> p.x) // 기준1: x
    				.thenComparing((Pair p) -> p.y); // 기준2: y
    		
    		list.sort(comparator);
    		
    		for(Pair p : list) {
    			System.out.println(p.x + " " + p.y);
    		}	
    	}
    }

     

     sort 메서드를 호출할 때 익명 클래스로 구현하던 Comparator를 제거했습니다. sort 메서드 외부에서 미리 구현해둔 Comparator 객체를 매개변수로 주입하면서 깔끔해졌습니다.