영운's 블로그

[Java] Comparator를 이용한 2차원 배열, 객체 배열 정렬(오름차순/내림차순) 본문

자바

[Java] Comparator를 이용한 2차원 배열, 객체 배열 정렬(오름차순/내림차순)

오영운(you88) 2022. 4. 13. 17:09

Comparator 인터페이스에 대해 알아보고 이를 이용하여 기본으로 제공하는 정렬보다 복잡한 2차원 배열, 객체 배열을 원하는 기준에 따라 오름차순, 내림차순 정렬을 구현해 보고자 한다.

 

Comparator란?

Comparator는 비교를 위한 인터페이스이다.

인터페이스이기에 인터페이스 구현을 통해 자신이 원하는대로 오버라이딩이 가능하다.

 

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

 

내부적인 Comparator 인터페이스의 정의는 다음 자바 공식 문서 링크를 통해 확인할 수 있다.

https://docs.oracle.com/javase/8/docs/api/index.html

 

Java Comparator interface document
이외에도 많은 함수가 Compator 인터페이스에 존재한다.

 

Comparator 인터페이스에는 굉장히 여러 메서드가 선언되어 있는데 우리가 실제로 오버라이딩 해야 하는 함수는

 

int compare(Object o1, Object o2) 함수 뿐이다.

 

인터페이스라 모든 메서드를 구현해야 하지만 compare()와 equals() 함수를 제외하고는 모두 static method 또는 default method이다. static/default method는 Java 8에 추가된 기능으로  static method는 기존의 static 기능이 인터페이스에 추가된 것이고, default method는 인터페이스 자체를 만들 때 미리 구현을 한 함수이다. 따라서 default method와 static method는 인터페이스 안의 메서드라도 별도로 구현이 필요 없다. equals 메서드의 경우 모든 클래스의 상위 클래스인 Object클래스에 구현되어 있기에 인터페이스임에도 별도로 구현할 필요는 없다. 

 

구체적인 compare() 메서드 오버라이딩은 아래에서 코드를 보며 자세히 서술한다.

 

 

Comparator는 왜 사용하는가?

 

배열과 여러 wrapper클래스는  Arrays.sort() 또는 Collections.sort() 등으로 정렬 기능을 제공한다.

하지만 단순히 배열, Collection framework(arraylist, arraylist..등등)만을 함수의 매개변수로 넣으면 다음과 같이 오름차순 정렬만 가능하다.

 

    public static void main(String[] args) {
        Integer[] arr = {7,6,1,9,5,4,8,3};

        Arrays.sort(arr);
        
        for(Integer e : arr)
            System.out.print(e + " ");

    }

 

-실행 결과-

 

하지만 Comparator를 이용하면 다음과 같은 다양한 형태의 정렬이 가능하다.

  • 개발자가 정의한 클래스 객체를 자신이 원하는 인스턴스 변수를 기준으로 정렬하기
  • 내림차순 정렬하기
  • 다차원 배열 정렬하기

 

 

Comparator 이용한 다차원 배열 내림차순/오름차순 정렬

import java.util.Arrays;
import java.util.Comparator;

public class Solution {
    public static void main(String[] args) {
        int[] answer = new int[100];

        Double[][] arr = {
                {1.0, 0.5},
                {2.0, 0.7},
                {3.0, 0.1},
                {4.0, 0.9},
                {5.0, 0.2}
        };

        /*2차원 배열 arr[][이거 기준] 정렬*/
        Arrays.sort(arr, new Comparator<Double[]>() {
            @Override
            public int compare(Double[] o1, Double[] o2) {
                return Double.compare(o2[1], o1[1]);
            }
        });

		/*내림차순 정렬된 2차원 배열 출력*/
        for (int i = 0; i < arr.length; i++) {
            System.out.print("{");
            for (int j = 0; j < arr[i].length; j++) {
                System.out.print(arr[i][j]);
                if(j+1 != arr[i].length)
                    System.out.print(", ");
            }
            System.out.print("}");
            System.out.println();
        }
    }
}

 

Double형 2차원 배열을 배열의 두 번째 열을 기준으로 내림차순 정렬한 코드이다. 

 

-실행 결과-

 

 

전체 코드에서 핵심은 이 부분이다.

        /*2차원 배열 arr[][이거 기준] 정렬*/
        Arrays.sort(arr, new Comparator<Double[]>() {
            @Override
            public int compare(Double[] o1, Double[] o2) {
                return Double.compare(o2[1], o1[1]);
            }
        });

Arrays.sort를 호출하며 매개변수로 배열arr과 Comparator 인터페이스를 구현하여 넘겨주고 있다. 

Comparator 인터페이스를 매개변수로 전달하는 동시에 구현하는 '익명 클래스'를 사용하고 있다. 이러한 경우 별도로 implemets Comparator 키워드등으로 이를 구현하는 클래스를 별도로 만들고 이를 객체로 생성하는 등의 번거로운 절차들을 생략하기에 실제로 Comparator 인터페이스는 이렇게 많이 사용한다.

 

Double형 배열을 정렬할 것이기에 compare함수의 매개변수는 Doube형 배열이고 return 값으로 Double.compare() 함수를 호출했다.

compare가 하는 기능은 간단하다. 두 매개변수를 인자로 받아 두 인자를 비교한다.

1) 첫 번째 인자가 더 크면 양수를

2) 동일하면 0을

3) 두 번째 인자가 더 크면 음수를

return하도록 하면 된다.

 

그런데 이러한 compare 매소드는 이미 Java.lang.Double.compare()로 구현되어 있어 우리는 이것을 그대로 사용하면 된다.  2차원 배열의 두 번째 열{   , 0.5},{   , 0.7},{   , 0.1},{   , 0.9},{   , 0.2}의 값을 기준으로 내림차순 정렬하고자 하기에 Double.compare()함수에 인자의 순서를 바꿔(o2, o1) 내림차순 정렬이 되도록 했고 index 0이 아닌 index 1을 전달하여(o2[1], o1[1]) 배열의 두 번째 열을 기준으로 정렬하도록 했다.

 

 

        Arrays.sort(arr, new Comparator<Double[]>() {
            @Override
            public int compare(Double[] o1, Double[] o2) {
                return Double.compare(o1[1], o2[1]);
            }
        });

오름차순 정렬을 하고 싶다면 다음과 같이 o1[]과 o2[] 의 순서를 순차적으로 매개변수로 전달하면 된다.

 

        Arrays.sort(arr, new Comparator<Double[]>() {
            @Override
            public int compare(Double[] o1, Double[] o2) {
                return Double.compare(o2[0], o1[0]);
            }
        });

2차원 배열의 첫 번째 열 {1.0,  }{2.0, }{3.0, }{4.0, }{5.0, }을 기준으로 내림차순 정렬하고 싶은 경우 다음과 같이 0번째 index의 값을 매개변수로 전달하면 된다.

 

return (int)Double.compare(o2[1], o1[1]);

주의할 점은 Comparator 인터페이스의 compare 함수의 return값이 int형으로 선언되었다고

별도로 위와 같이 형 변환해서는 안된다.

o2[1]=1.5 , o1[1]=0.5인 경우 값은 0.5인데 int로 형 변환되기에 0이 되어 전혀 의도하지 않은 값이 나온다.

 

Integer, int형 다차원 배열이라면 o2[1] - o1[1] 같은 형식으로 써도

별도로 손실되는 값이 없기에 괜찮다.

 

 

Comparator 이용한 객체 배열 내림차순/오름차순 정렬

 

import java.util.Arrays;
import java.util.Comparator;

public class Solution {
    public static void main(String[] args) {
        Node[] nodeArr = new Node[5];
        
        for (int i = 0; i < nodeArr.length; i++) {
            nodeArr[i] = new Node();
            nodeArr[i].num = i;
        }


        /*객체 배열 내림차순 정렬하기*/
        Arrays.sort(nodeArr, new Comparator<Node>() {
            @Override
            public int compare(Node o1, Node o2) {
                if(o2.num > o1.num)
                    return 1;
                else if (o2.num == o1.num)
                    return 0;
                else
                    return -1;
            }
        });

        System.out.println("객체 배열 num 인스턴스 변수 기준 내림차순 정렬");
        for (int i = 0; i < nodeArr.length; i++) {
            System.out.print(nodeArr[i].num + " ");
        }
    }
}

public class Node {
    int num;
    String str = "그냥 문자열";
}

- 실형 결과 -

객체 배열 정렬에서 주의할 것은 우리가 만든 클래스이기에 별도로 compare 매소드가 구현되어 있지 않아 직접 compare 함수를 구현해야 한다는 것이다. 하지만 compare함수 자체가 비교하여 크면 1, 동일하면 0, 작으면 -1을 return하는 간단한 함수라 크게 어렵지는 않다.

 

Comparator 인터페이스를 구현 시 클래스의 인스턴스 변수 중 정렬의 기준으로 삼고 싶은 인스턴스 변수로 compare 함수를 만들면 해당 변수를 기준으로 정렬을 진행한다.

 

 

 

Comments