-
자바(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 등의 메서드가 존재합니다.
반응형'language > JAVA' 카테고리의 다른 글
Stream, generate(), limit(), distinct(), sorted() (0) 2021.01.24 JAVA Enum(열거 타입) 값 비교하기 (0) 2021.01.22 JAVA 인터페이스(Interface) 다중상속 시 메서드 충돌 해결하기 (1) 2021.01.22 Optional<T> (0) 2020.05.07 Stream, 스트림 (0) 2020.05.06