ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자바(JAVA)에서 배열(Arrays), 리스트(List) 정렬하기
    language/JAVA 2021. 1. 22. 08:29
    반응형

    자바에서 정렬을 구현하는 Collections 클래스의 sort 메서드를 살펴보면 다음과 같은 모습입니다.

     

    public static <T extends Comparable<? super T>> void sort(List<T> list) {
        list.sort(null);
    }

     

    이처럼 파라미터로 받은 List 객체의 sort 메서드를 실행하고 있습니다. 그럼 List 인터페이스 안에 있는 sort 메서드를 살펴 보겠습니다.

     

    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();    
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

     

    List 인터페이스에 구현된 기본 메서드인 sort 는 내부적으로 다시 Arrays 클래스의 sort 를 다시 호출하고 있는 것을 볼 수 있습니다. List 인터페이스 에서는 리스트 자료구조에 담겨있는 값을 배열로 변환할 뿐입니다.

     

    int[] numbers = {5, 3, 1, 2, 4};
    Arrays.sort(numbers);
    
    List<Integer> numbers = Arrays.asList(5, 3, 1, 2, 4);
    Collections.sort(numbers);

     

    배열을 정렬하는 것은 위와 같이 Arrays.sort 메소드를 사용하면 됩니다. 리스트인 경우에는 Collections.sort 메서드나 리스트 자체의 sort 메서드를 사용할 수 있습니다.

     

    그렇다면 원시변수가 아닌 객체를 비교한다면 어떤 기준으로 비교하게 될까요? 그리고 정렬 순서를 역순으로 바꾸고 싶다면 어떻게 해야 할까요? 이 때 사용할 수 있는 방법은 Comparable 이나 Comparator 인터페이스를 사용하는 것입니다.

     

    객체 배열 또는 리스트를 정렬한다고 생각해 봅시다. 당연히 비교를 할 수 있는 규칙이 필요합니다. 만약 배열 또는 리스트 안에 있는 모든 클래스가 어떤 규칙을 통해 정려할지 합의했다면 정렬 알고리즘은 그 규칙을 통해 정렬을 수행할 것입니다. 이때 규칙을 정해 주는 것이 바로 Comparable 이나 Comparator 같은 인터페이스 입니다. 

     

    클래스가 자신이 갖고 있는 객체를 정렬하기 위해서는 Comparable 인터페이스를 구현해야 합니다. 이 인터페이스는 compareTo 라는 메서드를 가지고 있고 하나의 타입 파라미터를 받아 그 타입 파라미터를 통해 비교를 하게 됩니다. 이 때 이 메서드의 결과가 양수인지 음수인지를 통해 정렬의 순서를 정하게 됩니다.

     

    public class Student implements Comparable<Student> {
    
        public Student(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        int age;
        String name;
    
        @Override
        public int compareTo(Student other) {
            return this.age - other.age;
        }
    }
    List<Student> students = Arrays.asList(
            new Student(34, "Brad"),
            new Student(22, "Nicole"),
            new Student(29, "Jay"),
            new Student(20, "Tom")
    );
    
    Collections.sort(students);

     

    위 코드와 같이 Student 클래스에 Comparable 인터페이스를 구현하고 compareTo 메소드를 구현하면 age 필드의 값을 기준으로 오름차순 정렬되게 됩니다. 그럼 정렬 순서를 바꾸려면 어떻게 하면 될까요? 

     

    public class Student implements Comparable<Student> {
    
        public Student(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        int age;
        String name;
    
        @Override
        public int compareTo(Student other) {
            return other.age - this.age;
        }
    }

     

    위 코드 처럼 compareTo 메서드를 수정해 주면 됩니다. 원시변수 뿐 아니라 이미 compareTo 메서드가 구현된 다른 객체를 기준으로 정렬할 수도 잇습니다. 

     

    public class Student implements Comparable<Student> {
    
        public Student(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        int age;
        String name;
    
        @Override
        public int compareTo(Student other) {
            return this.name.compareTo(other.name);
        }
    }
    List<Student> students = Arrays.asList(
            new Student(34, "Brad"),
            new Student(22, "Nicole"),
            new Student(29, "Jay"),
            new Student(20, "Tom")
    );
    
    Collections.sort(students);

     

    위와 같이 코드를 수정하고 실행하게 되면 name 필드를 기준으로 오름차순 정렬되게 됩니다.

     

    이 Comparable 인터페이스를 구현하면 해당 클래스를 정렬하는 모든 경우에 이 정렬 기준이 적용되게 됩니다. 하지만 전체 코드에서 딱 한부분에서만 정렬을 사용 하거나 정렬 기준이 다를 때가 있습니다. 그 때 사용하는 것이 바로 Comparator 인터페이스 입니다.

     

    가령 위 Student 클래스를 name 필드의 길이 순서로 정렬한다고 할 때 다음과 같이 할 수 있습니다.

     

    students.sort((first, second) -> first.name.length() - second.name.length());

     

    람다식으로 통해 Comparator 인터페이스 내부에 있는 compare 메서드를 구현했습니다.

     

    Comparator 인터페이스 안에는 이런 메서드를 반환하는 정적 고차함수가 많이 있습니다. 위에 람다식은 아래와 같이 Comparator의 정적 메서드를 통해 대체할 수 있습니다.

     

    students.sort(Comparator.comparingInt(student -> student.name.length()));

     

    Comparator 인터페이스에는 비교 대상이 같을 때 추가로 비교할 수 있는 방법도 있습니다. thenComparing 메소드를 사용하면 됩니다.

     

    다음은 Student 클래스의 age 필드 순으로 먼저 정렬하고, age 가 같으면 이름 순으로 정렬하는 코드입니다.

     

    students.sort(Comparator.comparing(Student::getAge).thenComparing(Student::getName));

     

    마지막으로 오름차순과 내림차순을 정해주는 정적 메서드도 있습니다. 이름순으로 오름차순과 내림차순으로 정렬하는 코드는 다음과 같습니다.

     

    students.sort(Comparator.comparing(Student::getName, Comparator.naturalOrder()));
    
    students.sort(Comparator.comparing(Student::getName, Comparator.reverseOrder()));

     

    이 외에도 null 값을 리스트의 앞이나 뒤로 보낼 수 있는 nullsFirst, nullsLast 등의 메서드가 존재합니다.

    반응형
Designed by Tistory.