[Java] Collections.sort
정렬 알고리즘의 선택
Collections.sort(List), Collection.sort(List, Comparator), Arrays.sort(Object[])
JDK6 - Arrays.mergeSort 메서드를 이용
JDK7,8 - Arrays.legacyMergeSort와 ComparableTimSort.sort 메서드를 이용
Arrays.sort(double[]), Arrays.sort(int[]), Arrays.sort(char[]), Arrays.sort(long[]), Arrays.sort(float[]), Arrays.sort(byte[])
JDK6 - Arrays.sort1과 Arrays.sort2(실수형 자료) 메서드를 이용
JDK7,8 - DualPivotQuicksort.sort 메서드를 이용
코드 분석
Collections.sort(List), Collection.sort(List, Comparator), Arrays.sort(Object[])
OpenJDK 6-b27
배열로 변환한 후 Arrays.sort(Object[])로 정렬을 위임합니다.
즉 JDK6에서는 Collections.sort는 Arrays.mergeSort 메서드를 이용하여 정렬을 합니다.
OpenJDK 7u40-b43
정렬 알고리즘은 Arrays.sort(Object[])로 위임합니다.
LegacyMergeSort.userRequested를 통해 legacyMergeSort(Object[])와 ComparableTimSort.sort(Object[])중 하나를 사용합니다.
주석에는 broken comparators(?)의 호환성을 위해 merge sort 구현체가 사용될 수 있다고 합니다.
그 이외의 경우는 Tim sort를 이용하여 정렬합니다.
즉 JDK7이상의 버전에서는 Collections.sort는 Arrays.sort를 호출하고 결국은 ComparableTimSort.sort 메서드를 통해 정렬합니다.
Arrays.sort(double[]), Arrays.sort(int[]), Arrays.sort(char[]), Arrays.sort(long[]), Arrays.sort(float[]), Arrays.sort(byte[])
OpenJDK 6-b27
sort1 메서드를 통해 정렬합니다.
소수점이 들어갔을 경우는 sort2 메서드를 통해 정렬합니다.
즉 실수일 경우는 Arrays.sort2 메서드를 통해 그 외는 Arrays.sort1 메서드를 통해 정렬합니다.
OpenJDK 7u40-b43
JDK7이상의 버전에서는 DualPivotQuicksort.sort 메서드를 통해서 정렬합니다.
코드 분석(JDK 6) - Arrays.mergeSort()
Insertion sort와 Merge sort를 결합한 정렬 알고리즘을 사용합니다.
코드 분석(JDK 7,8) - ComparableTimSort.sort(Object[])
사용된 정렬 알고리즘
- Binary Insertion Sort - MIN_MERGE 이하
- Tim sort - MIN_MERGE 이상
Tim sort
run 생성
A natural run : List안에 존재하는 정렬된 sub-array입니다.
정방향, 역방향으로 연속된 run을 찾습니다. 역방향일 경우는 정방향으로 변환하여줍니다.
push
run의 시작점을 runBase라는 스택에 저장합니다.
run의 길이를 runLen이라는 스택에 저장합니다.
mergeCollapse
run의 length가 minRun보다 작은 경우, Binary insertion sort를 이용해 길이를 늘려줍니다.
length[top] + length[top-1] < length[top-2]
length[top] < length[top-1]
위 두 식을 만족할때까지 병합을 합니다.
이 부분 때문에 O(n log n)을 보장(?)한다고 합니다.
이웃하는 두 개의 run을 병합하니다.
두 run은 모두 정렬되어 있는 상태이기 때문에
앞에 있는 run을 복사해서 앞 run의 마지막 값보다 작아지는 뒷 run의 인덱스 다음에 복사하면 뒷 run의 요소 중에 앞 run의 끝보다 큰 부분은 정렬을 생략할 수 있습니다.
마찬가지로 뒷 run을 복사해서 뒷 run의 처음 값보다 커지는 앞 run의 인덱스 전에 복사하면 앞 run의 요소 중에 뒷 run의 앞보다 작은 부분은 정렬을 생략할 수 있습니다.
위에 삽입할 위치를 검색해서 생략을 하는 과정을 gallop mode라 합니다. 이후는 두 값을 비교하며 정렬합니다.
만약 남은 두 run중에 한 곳에서 연속해서 삽입할 값이 선택되는 경우는 다시 위에서 진행한 gallop mode를 통해 정렬을 생략하는 시도를 합니다.
결론
랜덤 데이터보다는 정렬이 되있는 데이터들에 정렬을 생략할 수 있기 때문에 더 좋은 성능을 낼 수 있습니다.
Worst case도 O(n log n)를 보장합니다.
Merge sort + Insertion Sort
이미지 출처 : http://en.wikipedia.org/wiki/Timsort
코드 분석(JDK 6) - Arrays.sort1(), Arrays.sort2()
JDK 6에서 Array.sort()를 호출할 경우
Arrays.sort1(), Arrays.sort2() 두 종류의 sort함수가 내부적으로 수행된다.
Arrays.sort1()
정렬 알고리즘이 배열의 크기에 따라 나뉘는데 배열의 크기가 7개 이하일 경우 insertion sort를 호출.
그 외의 경우 quick sort
Arrays.sort2()의 경우 float, double과 같이 실수(부동소수)data type을 정렬할 때 호출된다.
sort2(float[] a)
sort2(double[] a)
정렬과정에서 -0.0과 NaN 비교하는 불필요한 연산을 피하기 위해 이를 0.0(float은 0.0f, double은 0.0D)으로 전환해주기 위해 내부적으로 int i = Float.floatToIntBits(-0.0F)이나 long l = Double.doubleToLongBits(-0.0D) 를 호출한다. 배열의 -0.0, NaN을 수정후 sort1()을 호출한다.
코드 분석(JDK 7,8) - DualPivotQuicksort.sort(int[], int, int)
JDK 1.8 Arrays.sort() 에서는 내부적으로 Dual-Pivot QuickSort 을 사용한다. O( n log( n )) 효율.
DualPivotQuicksort에서도 무조건 DualPivotQuicksort를 수행하는것이 아니라
정렬해야할 대상의 크기에 따라 insertion/merge 정렬을 수행할 수 있다.
우선 일반적인 quick 정렬이 Pivot이 하나를 사용한다면
DualPivotQuicksort은 이름에서 알 수 있듯 Pivot을 두 개 사용한다.
수도코드는 다음과 같다.
자세한 절차는 아래 링크에서 확인할 수 있다.
http://www.slideshare.net/sebawild/average-case-analysis-of-java-7s-dual-pivot-quicksort
# 성능 분석
시간 복잡도
Timsort | Introsort | Merge sort | Quicksort | Insertion sort | Selection sort | Smoothsort | |
Best case | O(n) | O(n log n) \ | O(n log n) | O(n) | O(n^2) | O(n) | |
Average case | O(n log n) | O(n log n) | O(n log n) | O(n log n) | O(n^2) | O(n^2) | O(n log n) |
Worst case | O(n log n) | O(n log n) | O(n log n) | O(n^2) | O(n^2) | O(n^2) | O(n log n) |
공간 복잡도
Timsort | Merge sort | Quicksort | Insertion sort | Selection sort | Smoothsort | |
Space complexity | O(n) | O(n) | O(log n) | O(1) | O(1) | O(1) |
출처 : 위키피디아
참고 사이트
OpenJDK-7u40-b43(Grepcode)
Timsort(Wikipedia)
MergeSort(VISUALGO)