자바에서 배열에 대해 정렬을 하고싶다면 어떻게해야할까?
코드를 통해 살펴보자
import java.util.Arrays;
public class SortMain1 {
public static void main(String[] args) {
Integer[] array = {3, 2, 1};
System.out.println(Arrays.toString(array));
System.out.println("기본 정렬 후");
Arrays.sort(array);
System.out.println(Arrays.toString(array));
}
}
실행결과
[3, 2, 1]
기본 정렬 후
[1, 2, 3]
util패키지의 Arrays.sort를 사용하면 배열의 데이터를 정렬할 수 있다.
정렬을 할 때 사용되는 정렬알고리즘은 다양하게 있지만 자바는 데이터가 32개 이하일 때는 듀얼 피벗 퀵소트를 사용하고, 데이터가 많을 때는 팀소트를 사용한다. 이런 알고리즘의 성능은 평균 O(log N)의 시간복잡도로 제공된다.
만약 배열을 정렬하는데 1->2->3의 순서가아닌 3->2->1과 같이 역순으로 정렬하고싶다면 어떻게 해야할까?
이런 경우에 사용되는 인터페이스가 바로 Comparator이다.
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparator는 compare라는 메서드를 제공하는데 이 메서드는 int타입을 반환한다.
만약 첫번째 인수가 작으면 음수를 반환하고, 두 값이 같다면 0, 첫번쨰 인수가 더 크다면 양수를 반환하게된다. 이 반환값을 기준으로 원하는대로 배열을 정렬시킬 수 있다.
Comparator를 구현하여 오름차순, 내림차순 구현하기
import java.util.Arrays;
import java.util.Comparator;
public class SortMain2 {
public static void main(String[] args) {
Integer[] array = {3, 2, 1};
System.out.println(Arrays.toString(array));
System.out.println("Comparator 비교");
Arrays.sort(array, new AscComparator());
System.out.println(Arrays.toString(array));
System.out.println();
Arrays.sort(array, new DescComparator());
System.out.println(Arrays.toString(array));
System.out.println();
Arrays.sort(array, new AscComparator().reversed());
System.out.println(Arrays.toString(array));
}
static class AscComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("o1 = " + o1 + " o2=" + o2);
return (o1 < o2) ? -1 : ((o1 == o2) ? 0 : 1);
}
}
static class DescComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("o1 = " + o1 + " o2=" + o2);
return (o1 < o2) ? -1 : ((o1 == o2) ? 0 : 1) * -1;
}
}
}
Arrays.sort()메서드의 매개변수로 Comparator를 구현한 클래스를 넣어줄 수 있다.
이 때 compare()메서드를 재정의하여 원하는대로 정렬의 기준을 바꿀 수 있다.
참고로 이중 삼항연산자가 어색할 수 있는데 if문으로 변경하면 아래와 같이 구현된다.(오름차순 기준)
int result;
if (o1 < o2) {
result = -1;
} else if (o1 == o2) {
result = 0;
} else {
result = 1;
}
이렇게 Comparator(비교자)를 사용하여 정렬의 기준을 자유롭게 변경할 수 있다.
문자도 숫자를 정렬하는 개념과 같이 ABC(오름차순), CBA(내림차순)과 같이 정렬할 수 있으며 자바에서 Integer클래스와 String클래스에 이미 구현을 해놓았다.
하지만 만약 내가 정의한 클래스 예를들면 Member, Order와 같은 객체들은 어떻게 정렬할 수 있을까?Member클래스를 정렬한다고하면 id값으로 정렬을 할수도있고, name값으로 정렬을 할수도있을 것이다.
이 때는 Comparable인터페이스를 구현하면 객체를 비교할수 있도록하여 자유롭게 정렬할 수 있도록 한다.
public interface Comparable<T> {
public int compareTo(T o);
}
Comparable인터페이스는 compareTo()라는 메서드를 제공한다. 이 메서드를 내가 만든 객체(Member)에서 재정의하여 정렬의 기준을 정할 수 있다.compareTo메서드도 int타입으로 반환하는데, 현재 객체가 인수로 주어진 객체보다 더 작으면 음수를, 두 객체의 크기가 같다면 0, 현재 객체가 인수로 주어진 객체보다 더 크면 양수를 반환한다.
회원의 나이를 정렬의 기준값으로 정의
public class MyMember implements Comparable<MyMember>{
private String id;
private int age;
public MyMember(String id, int age) {
this.id = id;
this.age = age;
}
public String getId() {
return id;
}
public int getAge() {
return age;
}
@Override
public int compareTo(MyMember o) {
return this.age < o.age ? -1 : ((this.age == o.age) ? 0 : 1);
}
@Override
public String toString() {
return "MyMember{" + "id='" + id + '\'' + ", age=" + age + '}';
}
}
compareTo()메서드만 살펴보자
위에서 정수값을 정렬할 때와 같으며 단지 비교하는 대상을 객체자신의 age필드와 파라미터의 age로 변경했을 뿐이다.
이렇게 Comparable을 통해 구현한 순서를 자연순서라고 한다.
import java.util.Arrays;
public class SortMain3 {
public static void main(String[] args) {
MyMember aMember = new MyMember("a", 30);
MyMember bMember = new MyMember("b", 20);
MyMember cMember = new MyMember("c", 10);
MyMember[] members = {aMember, bMember, cMember};
System.out.println(Arrays.toString(members));
System.out.println();
System.out.println("재정의한 Comparable로 정렬");
Arrays.sort(members);
System.out.println(Arrays.toString(members));
}
}
실행결과
[MyMember{id='a', age=30}, MyMember{id='b', age=20}, MyMember{id='c', age=10}]
재정의한 Comparable로 정렬
[MyMember{id='c', age=10}, MyMember{id='b', age=20}, MyMember{id='a', age=30}]
실행결과를 살펴보면 나이순서대로 데이터가 정렬된 것을 확인할 수 있다.
여기서 핵심은 MyMember클래스 안에 Comparable을 구현한 재정의 메서드 compareTo는 해당 클래스타입의 배열을 정렬할 때 기본값으로 사용된다.
만약 특별한 경우에 나이의 역순으로 정렬하고싶다거나, id를 기준으로 정렬을 하고싶다면 Comparator를 구현하는 구현클래스를 따로 만들어주어야한다.
정리하자면
Comparable인터페이스는 클래스 자체에 기본 정렬 기준을 정의하며compareTo메서드를 재정의 하면Arrays.sort()메서드를 호출할 때의 기준을 정하게된다.Comparator인터페이스는 기본 정렬 기준과는 다른 기준으로 정렬을 할 때 사용한다.compare메서드를 재정의한 뒤,Arrays.sort(정렬하고자하는 배열,new compare()를 재정의한 정렬기준 클래스)와 같이 인자를 주면 원하는 정렬기준으로 정렬할 수 있다.
멤버별 나이의 역순으로 정렬
public class DescAgeComparator implements Comparator<MyMember> {
@Override
public int compare(MyMember o1, MyMember o2) {
return (o1.getAge() < o2.getAge()) ? -1 : ((o1.getAge() == o2.getAge()) ? 0 : 1) * -1;
}
}
import java.util.Arrays;
public class SortMain3 {
public static void main(String[] args) {
MyMember aMember = new MyMember("a", 30);
MyMember bMember = new MyMember("b", 20);
MyMember cMember = new MyMember("c", 10);
MyMember[] members = {aMember, bMember, cMember};
System.out.println(Arrays.toString(members));
System.out.println();
System.out.println("재정의한 Comparable로 정렬");
Arrays.sort(members);
System.out.println(Arrays.toString(members));
Arrays.sort(members, new DescAgeComparator());
System.out.println("나이의 역순으로 정렬");
System.out.println(Arrays.toString(members));
}
}
실행결과
나이의 역순으로 정렬
[MyMember{id='a', age=30}, MyMember{id='b', age=20}, MyMember{id='c', age=10}]멤버의 아이디를 기준으로 오름차순정렬
import java.util.Comparator;
public class IdComparator implements Comparator<MyMember> {
@Override
public int compare(MyMember o1, MyMember o2) {
return o1.getId().compareTo(o2.getId());
}
}
이미 재정의 된 MyMember의 compareTo메서드를 사용하여 간단하게 정렬기준을 재정의할 수 있다.
또한 역순정렬을 원할 때 따로 클래스를 정의할 필요없이 reverse()메서드를 사용하여 간단하게 역순으로 정렬할 수 있다.
Id로 오름차순/내림차순
import java.util.Arrays;
public class SortMain3 {
public static void main(String[] args) {
MyMember aMember = new MyMember("a", 30);
MyMember bMember = new MyMember("b", 20);
MyMember cMember = new MyMember("c", 10);
MyMember[] members = {aMember, bMember, cMember};
System.out.println(Arrays.toString(members));
System.out.println();
Arrays.sort(members, new IdComparator());
System.out.println("아이디순으로 오름차순");
System.out.println(Arrays.toString(members));
System.out.println("아이디순으로 내림차순");
Arrays.sort(members, new IdComparator().reversed());
System.out.println(Arrays.toString(members));
}
}
실행결과
아이디순으로 오름차순
[MyMember{id='a', age=30}, MyMember{id='b', age=20}, MyMember{id='c', age=10}]
아이디순으로 내림차순
[MyMember{id='c', age=10}, MyMember{id='b', age=20}, MyMember{id='a', age=30}]참고로 Arrays.sort를 호출할 때 Comparator가 구현되어있지 않으면 자동적으로 클래스 내부에 기본으로 정의된 Comparable을 사용하는데 Comparable도 정의되어있지 않다면 예외가 발생한다.
#### 컬렉션프레임워크의 정렬
컬렉션프레임워크도 배열과 같은 개념으로 정렬기준을 제공한다.
물론 순서가 있는 List같은 자료구조에만 제공되며 Map, Set과 같은 자료구조는 순서가 없기때문에 정렬을 제공하지 않는다.Tree자료구조 같은경우에는 데이터를 보관할 때 데이터를 정렬하면서 보관하기때문에 정렬기준을 반드시 명시해야한다.
Collections.sort(list)- 기본 정렬을 제공한다.
- 객체지향을 강조하기위해서는 잘 사용되지 않는다.
list.sort(null)- 별도의 비교자가 없기때문에
Comparable로 비교해서 정렬한다
- 별도의 비교자가 없기때문에
Collecions.sort(list, new IdComparator)- 별도의 비교자로 비교할 수 있지만 이 메서드 또한 잘 사용되지 않고
list.sort()를 사용한다.
- 별도의 비교자로 비교할 수 있지만 이 메서드 또한 잘 사용되지 않고
list.sort(new IdComparator())- 전달한 비교자로 비교한다.
'Language > Java' 카테고리의 다른 글
| [Java] 람다 표현식과 함수 인터페이스 (0) | 2024.11.08 |
|---|---|
| [JAVA] 예외 계층 (0) | 2024.07.09 |
| 해시 알고리즘 (0) | 2024.06.29 |
| [JAVA - 자료구조] HashSet (0) | 2024.06.29 |
| [JAVA] 불변객체 (0) | 2024.06.24 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!