Java Comparable 및 Comparator 예제

Java에서의 Comparable 및 Comparator는 객체의 컬렉션을 정렬하는 데 매우 유용합니다. Java는 기본 타입 배열이나 Wrapper 클래스 배열 또는 목록을 정렬하기 위한 몇 가지 내장 메서드를 제공합니다. 여기서 우리는 먼저 기본 타입 및 래퍼 클래스의 배열/목록을 정렬하는 방법을 배우고, 그런 다음 java.lang.Comparable 및 java.util.Comparator 인터페이스를 사용하여 사용자 지정 클래스의 배열/목록을 정렬하는 방법을 배우겠습니다. 간단한 프로그램으로 기본 타입이나 객체 배열 및 목록을 어떻게 정렬할 수 있는지 살펴보겠습니다.

package com.journaldev.sort;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class JavaObjectSorting {

    /**
     * This class shows how to sort primitive arrays, 
     * Wrapper classes Object Arrays
     * @param args
     */
    public static void main(String[] args) {
        //int 배열과 같은 기본 타입 배열 정렬
        int[] intArr = {5,9,1,10};
        Arrays.sort(intArr);
        System.out.println(Arrays.toString(intArr));
        
        //문자열 배열 정렬
        String[] strArr = {"A", "C", "B", "Z", "E"};
        Arrays.sort(strArr);
        System.out.println(Arrays.toString(strArr));
        
        //Wrapper 클래스의 객체 목록 정렬
        List strList = new ArrayList();
        strList.add("A");
        strList.add("C");
        strList.add("B");
        strList.add("Z");
        strList.add("E");
        Collections.sort(strList);
        for(String str: strList) System.out.print(" "+str);
    }
}

위 프로그램의 출력은 다음과 같습니다:

[1, 5, 9, 10]
[A, B, C, E, Z]
 A B C E Z

이제 객체 배열을 정렬해 보겠습니다.

package com.journaldev.sort;

public class Employee {

    private int id;
    private String name;
    private int age;
    private long salary;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public long getSalary() {
        return salary;
    }

    public Employee(int id, String name, int age, int salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    //이는 사용자 친화적인 정보를 출력하기 위해 재정의됩니다.
    public String toString() {
        return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
                this.salary + "]";
    }

}

나는 Employee 객체 배열을 정렬하기 위해 사용한 코드는 다음과 같습니다.

//객체 배열 정렬
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);

//Comparable 인터페이스 구현을 사용하여 직원 배열 정렬
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));

이를 실행하려고 할 때 다음과 같은 런타임 예외가 발생합니다.

Exception in thread "main" java.lang.ClassCastException: com.journaldev.sort.Employee cannot be cast to java.lang.Comparable
	at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:290)
	at java.util.ComparableTimSort.sort(ComparableTimSort.java:157)
	at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
	at java.util.Arrays.sort(Arrays.java:472)
	at com.journaldev.sort.JavaSorting.main(JavaSorting.java:41)

Comparable 및 Comparator

자바는 Comparable 인터페이스를 제공합니다. 이 인터페이스는 ArraysCollections 정렬 메소드를 사용하려면 사용자 정의 클래스에서 구현해야 합니다. Comparable 인터페이스에는 정렬 메소드에서 사용되는 compareTo(T obj) 메소드가 있습니다. 이를 확인하기 위해 래퍼, 문자열 또는 날짜 클래스를 확인할 수 있습니다. 이 메소드를 재정의하여 “this” 객체가 인수로 전달된 객체보다 작으면 음의 정수, 같으면 0, 크면 양의 정수를 반환하도록 해야 합니다. Employee 클래스에 Comparable 인터페이스를 구현한 후, 결과적으로 만들어진 Employee 클래스는 다음과 같습니다.

package com.journaldev.sort;

import java.util.Comparator;

public class Employee implements Comparable {

    private int id;
    private String name;
    private int age;
    private long salary;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public long getSalary() {
        return salary;
    }

    public Employee(int id, String name, int age, int salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public int compareTo(Employee emp) {
        //사원을 id 오름차순으로 정렬해 봅시다.
        //이 사원 id가 
        //지정된 객체보다 작으면 음의 정수, 같으면 0, 크면 양의 정수를 반환합니다.
        return (this.id - emp.id);
    }

    @Override
    //이는 Employee에 대한 사용자 친화적인 정보를 출력하는 데 필요합니다.
    public String toString() {
        return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
                this.salary + "]";
    }

}

이제 위의 코드 조각을 실행하여 사원들을 Arrays 정렬하고 출력하면 다음과 같은 결과가 나옵니다.

Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]

직원 배열이 ID로 오름차순으로 정렬되어 있는 것을 볼 수 있습니다. 그러나 대부분의 현실적인 상황에서는 다른 매개변수를 기준으로 정렬을 원합니다. 예를 들어 CEO로서 저는 급여를 기준으로 직원을 정렬하고, 인사 담당자는 나이를 기준으로 정렬하고 싶을 것입니다. 이런 상황에서 Java Comparator 인터페이스를 사용해야 합니다. 왜냐하면 Comparable.compareTo(Object o) 메서드 구현은 기본 정렬을 제공하며 동적으로 변경할 수 없기 때문입니다. 그러나 Comparator를 사용하면 다양한 정렬 방법을 가진 여러 메서드를 정의하고 필요에 따라 정렬 방법을 선택할 수 있습니다.

Java Comparator

Comparator 인터페이스의 compare(Object o1, Object o2) 메서드를 구현해야 합니다. 이 메서드는 두 개의 객체 인수를 사용하며 첫 번째 인수가 두 번째 인수보다 작으면 음수를 반환하고, 동일하면 0을 반환하며, 첫 번째 인수가 두 번째 인수보다 크면 양수를 반환해야 합니다. Comparable 및 Comparator 인터페이스는 컴파일 시간 유형 검사를 위해 제네릭을 사용합니다. Java Generics에 대해 자세히 알아보세요. Employee 클래스에서 다양한 Comparator 구현을 만드는 방법은 다음과 같습니다.

/**
     * Comparator to sort employees list or array in order of Salary
     */
    public static Comparator<Employee> SalaryComparator = new Comparator<Employee>() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return (int) (e1.getSalary() - e2.getSalary());
        }
    };

    /**
     * Comparator to sort employees list or array in order of Age
     */
    public static Comparator<Employee> AgeComparator = new Comparator<Employee>() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getAge() - e2.getAge();
        }
    };

    /**
     * Comparator to sort employees list or array in order of Name
     */
    public static Comparator<Employee> NameComparator = new Comparator<Employee>() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getName().compareTo(e2.getName());
        }
    };

위의 모든 Comparator 인터페이스 구현은 익명 클래스입니다. 우리는 이들 비교자를 배열과 컬렉션 클래스의 sort 함수에 인수로 전달하는데 사용할 수 있습니다.

//월급으로 직원 배열 정렬하기 용이한 Comparator 사용
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));

//나이로 직원 배열 정렬하기 용이한 Comparator 사용
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));

//이름으로 직원 배열 정렬하기 용이한 Comparator 사용
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));

다음은 위의 코드 스니펫의 출력입니다:

Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]

이제 우리는 자바 객체 배열이나 목록을 정렬하려면 우리는 기본 정렬을 제공하기 위해 자바 Comparable 인터페이스를 구현해야 하고, 우리는 다양한 정렬 방법을 제공하기 위해 자바 Comparator 인터페이스를 구현해야 함을 알았습니다. 우리는 또한 Comparator 인터페이스를 구현하는 별도의 클래스를 생성하고 사용할 수 있습니다. 다음은 자바에서 Comparable과 Comparator에 대해 설명하는 최종 클래스입니다.

package com.journaldev.sort;

import java.util.Comparator;

public class Employee implements Comparable {

    private int id;
    private String name;
    private int age;
    private long salary;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public long getSalary() {
        return salary;
    }

    public Employee(int id, String name, int age, int salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public int compareTo(Employee emp) {
        //아이디에 기초하여 사원을 오름차순으로 정렬합시다
        //이 사원 id가
        //지정된 객체보다 작거나, 같거나, 큰 경우 음의 정수, 제로 또는 양의 정수를 반환한다.
        return (this.id - emp.id);
    }

    @Override
    //이것은 직원에 대한 사용자 친화적인 정보를 출력하는 데 필요합니다
    public String toString() {
        return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
                this.salary + "]";
    }

    /**
     * Comparator to sort employees list or array in order of Salary
     */
    public static Comparator SalaryComparator = new Comparator() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return (int) (e1.getSalary() - e2.getSalary());
        }
    };

    /**
     * Comparator to sort employees list or array in order of Age
     */
    public static Comparator AgeComparator = new Comparator() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getAge() - e2.getAge();
        }
    };

    /**
     * Comparator to sort employees list or array in order of Name
     */
    public static Comparator NameComparator = new Comparator() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getName().compareTo(e2.getName());
        }
    };
}

여기에 두 사원 객체를 먼저 id로 비교하고 같으면 이름으로 비교하는 Comparator 인터페이스의 별도 클래스 구현이 있습니다.

package com.journaldev.sort;

import java.util.Comparator;

public class EmployeeComparatorByIdAndName implements Comparator<Employee> {

    @Override
    public int compare(Employee o1, Employee o2) {
        int flag = o1.getId() - o2.getId();
        if(flag==0) flag = o1.getName().compareTo(o2.getName());
        return flag;
    }

}

다음은 우리가 Comparable과 Comparator를 사용하여 자바에서 객체를 다양한 방법으로 정렬하는 테스트 클래스입니다.

package com.journaldev.sort;

import java.util.Arrays;

public class JavaObjectSorting {

    /**
     * This class shows how to sort custom objects array/list
     * implementing Comparable and Comparator interfaces
     * @param args
     */
    public static void main(String[] args) {

        //사용자 지정 개체 배열
        Employee[] empArr = new Employee[4];
        empArr[0] = new Employee(10, "Mikey", 25, 10000);
        empArr[1] = new Employee(20, "Arun", 29, 20000);
        empArr[2] = new Employee(5, "Lisa", 35, 5000);
        empArr[3] = new Employee(1, "Pankaj", 32, 50000);
        
        //Comparable 인터페이스 구현을 사용하여 직원 배열 정렬
        Arrays.sort(empArr);
        System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
        
        //Comparator를 사용하여 급여로 직원 배열 정렬
        Arrays.sort(empArr, Employee.SalaryComparator);
        System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
        
        //Comparator를 사용하여 나이로 직원 배열 정렬
        Arrays.sort(empArr, Employee.AgeComparator);
        System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
        
        //Comparator를 사용하여 이름으로 직원 배열 정렬
        Arrays.sort(empArr, Employee.NameComparator);
        System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
        
        //Comparator 클래스를 사용하여 ID 및 이름으로 정렬된 직원 목록
        empArr[0] = new Employee(1, "Mikey", 25, 10000);
        Arrays.sort(empArr, new EmployeeComparatorByIdAndName());
        System.out.println("Employees list sorted by ID and Name:\n"+Arrays.toString(empArr));
    }

}

위 프로그램의 출력은 다음과 같습니다:

Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by ID and Name:
[[id=1, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000]]

java.lang.Comparablejava.util.Comparator은 자바에서 객체를 정렬하는 데 사용할 수 있는 강력한 인터페이스입니다.

Comparable 대 Comparator

  1. Comparable 인터페이스는 단일 정렬 방법을 제공하는 데 사용되며 Comparator 인터페이스는 다양한 정렬 방법을 제공하는 데 사용됩니다.
  2. Comparable을 사용하려면 클래스에서 구현해야 하지만 Comparator를 사용하려면 클래스를 변경할 필요가 없습니다.
  3. Comparable 인터페이스는 java.lang 패키지에 있으며 Comparator 인터페이스는 java.util 패키지에 있습니다.
  4. Comparable을 사용하려면 클라이언트 측에서 코드 변경이 필요하지 않습니다. Arrays.sort() 또는 Collection.sort() 메서드는 자동으로 클래스의 compareTo() 메서드를 사용합니다. Comparator의 경우 클라이언트는 compare() 메서드에서 사용할 Comparator 클래스를 제공해야 합니다.

알고 계신가요? Comparator 인수를 취하는 Collections.sort() 메서드가 Strategy Pattern을 따르는지요?

완전한 코드 및 더 많은 코어 자바 예제는 GitHub Repository에서 확인할 수 있습니다.

Source:
https://www.digitalocean.com/community/tutorials/comparable-and-comparator-in-java-example