-
Notifications
You must be signed in to change notification settings - Fork 0
java basic
java에서는 다양한 정렬 방법을 제시한다.
- 직접 구현
- Arrays.sort()
- Collections.sort()
- Comparator 구현
직접 정렬 알고리즘을 구현한다. 이는 다른 파트에서 설명하기 때문에 생략한다.
[JAVA]Arrays.sort()의 내부 동작(1) - 자바니또 참조
primitive type의 배열일 때와 object type의 배열일 때 각각 내부 동작이 다르다.
- primitive: DualPivotQuicksort
- collection(object): TimSort
대표적인 배열인 int[]
의 경우 내부에서 DualPivotQuicksort.sort()
를 호출한다.
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
즉, pivot을 2개 사용하는 quick sort
를 사용하는 것을 확인할 수 있다.
quick sort는 평소엔 O(nlogn)을 보장하지만, 정렬이 거의 다 된 상태라면 최악의 경우로써 O(n^2)이 될 수 있다. pivot을 2개 사용하면 quick sort보다 O(nlogn)을 더욱 보장할 수 있다. (하지만 이 경우도 완전히 보장하진 않는다.)
collection의 경우 각 내부에서 sort() 함수를 자체적으로 지원하는 경우가 많다. 이는 대부분 Arrays util 라이브러리를 사용한다.
제일 많이 사용하는 List
의 경우 각 sort() 함수 내부에 Arrays.sort()
를 호출하는 것을 확인할 수 있다.
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
modCount++;
}
Arrays.sort()
내부는 tim sort로 구현되있는 것을 알 수 있다.
TimSort는 binary search
(이진삽입정렬) + merge sort
로 이루어져있고, 정렬이 거의 다 된 상태라면 O(n)을 정렬이 안 된 상태라도 O(nlogn)의 시간복잡도를 보장한다.
public static <T> void sort(T[] a, int fromIndex, int toIndex,
Comparator<? super T> c) {
if (c == null) {
sort(a, fromIndex, toIndex);
} else {
rangeCheck(a.length, fromIndex, toIndex);
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex, c);
else
TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
}
}
또다른 java util 라이브러리인 Collections에서 지원하는 정렬이다. 이름 그대로 Collection들의 sort를 지원한다.
당연하게도 Collection 중에 순서가 있는 저장 공간인 List
인터페이스를 상속한 자료구조만 가능하다.
대표적으로 LinkedList
, Stack
, Vector
, ArrayList
등이 있다.
Collections.sort()
내부는 마찬가지로 Arrays.sort()
를 호출하는 것을 확인할 수 있다.
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
매개변수로 Comparator를 넣었을 때 내부 코드이다.
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);
}
}
결국 위 Arrays.sort()
와 같은 원리로 동작하게 된다.
정렬 방식을 맘대로 구성할 수 있는 방법이 존재한다.
- Comparator 구현체
- Comparable 구현
primitive type, collection 둘 다 사용 가능하다.
primitive type이 아닌 Object type으로 직접 만들어서 Collection을 사용하여 자료구조를 만드는 경우가 많다.
- 좌표 객체 생성
- 사람의 신체
- ...
이러한 경우에 이 Comparator를 구현하면 입맛에 맞게 커스텀할 수 있다.
❗Collection 정렬일 경우 무조건 구현해야 한다!
직접 Comparator
객체를 생성하여 매개변수로 전달해야 하는데 필수적으로 compart() 함수를 다음과 같이 override해야한다.
persons.sort(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o2.height - o1.height;
}
});
이 때 compare 함수의 return으로 사용되는 값은 o2를 기준으로 비교한다. return의 값이 양수냐 음수냐에 따라 오름차순, 내림차순이 결정된다.
- 오름차순: 첫번째 인자가 두번째 인자보다 작다면 음수, 같다면 0, 크다면 양수를 리턴하도록 구현
- 내림차순: 위와 반대로 구현
본 예제는 내림차순으로 구현한 예이다.
lambda 함수를 사용하여 코드를 간단하게 만들 수 있다.
persons.sort((o1, o2) -> o2.height - o1.height);