Collections.sort는 Mergesort를 사용하지만 Arrays.sort는 사용하지 않는 이유는 무엇입니까?


95

JDK-8 (x64)을 사용하고 있습니다. 들어 Arrays.sort(프리미티브) 나는 자바 문서에 다음과 발견 :

정렬 알고리즘은 Vladimir Yaroslavskiy, Jon Bentley, Joshua Bloch 의 Dual-Pivot Quicksort입니다.

들어 Collections.sort(객체) 나는이 "Timsort"를 발견 :

이 구현은 안정적이고 적응 적이며 반복적 인 mergesort입니다 .이 구현 은 지정된 목록을 배열로 덤프하고 배열을 정렬 하며 목록을 반복 하여 배열 의 해당 위치에서 각 요소를 재설정합니다.

Collections.sort어레이를 사용하는 경우 Arrays.sort듀얼 피벗 QuickSort를 호출 하거나 사용 하지 않는 이유는 무엇입니까? Mergesort를 사용하는 이유는 무엇 입니까?


8
이것이 원시 배열에 대한 javadoc입니다. 객체 배열은 meregsort를 사용하여 정렬됩니다.
assylias

2
머지 소트주는 유 퀵 언젠가주고 nlogn2 geneally 배열 크기가 크지 만 컬렉션을 쉽게 nlogn2의 위험을 감수 있도록 항목 수백만 개까지가는 것이 아니다 수도 있지만 항상 nlogn 내가 N의 sqaure 의미 nlogn2 가치 PS 아니다
쿠마 Saurabh

quicksort의 O (n ^ 2)는 최악의 경우입니다. 실제로는 더 빠릅니다
James Wierzba 2015-09-01

하지만 u는 API를 만드는 동안 그 caese를 무시할 수 없습니다
Kumar Saurabh

2
이 링크 는 매우 관련이 있습니다.
qartal

답변:


99

API는 Quicksort 가 제공하지 않는 안정적인 정렬을 보장 합니다. 그러나 기본 값 을 자연 순서로 정렬 할 때 기본 값에는 동일성이 없기 때문에 차이를 느끼지 못할 것입니다. 따라서 Quicksort 는 기본 배열에 사용할 수 있으며 더 효율적인 것으로 간주 될 때 사용됩니다 ¹ .

객체의 경우 equals구현 또는 제공된 항목 에 따라 동일하다고 간주되는 다른 ID를 가진 객체 Comparator의 순서 가 변경 될 때 알 수 있습니다 . 따라서 Quicksort 는 옵션이 아닙니다. 따라서 MergeSort 의 변형 이 사용되며 현재 Java 버전은 TimSort를 사용 합니다 . 이는 Arrays.sort및 에 모두 적용 Collections.sort되지만 Java 8에서는 List자체가 정렬 알고리즘을 재정의 할 수 있습니다.


¹ Quicksort 의 효율성 이점은 제자리에서 수행 할 때 필요한 메모리가 적다는 것입니다. 그러나 극적으로 최악의 경우 성능을 가지고 있으며 TimSort 가 수행 하는 어레이에서 사전 정렬 된 데이터 실행을 악용 할 수 없습니다 .

따라서 정렬 알고리즘은 현재 잘못 명명 된 class를 유지하면서 버전마다 재 작업되었습니다 DualPivotQuicksort. 또한 문서는 필요하지 않을 때 사양에서 내부적으로 사용되는 알고리즘의 이름을 지정하는 것이 일반적으로 나쁜 생각이라는 것을 보여주지 못했습니다.

현재 상황 (Java 8 ~ Java 11 포함)은 다음과 같습니다.

  • 일반적으로 기본 배열의 정렬 방법은 특정 상황에서만 Quicksort 를 사용 합니다. 더 큰 어레이의 경우 TimSort 처럼 미리 정렬 된 데이터의 실행을 먼저 식별하고 실행 횟수가 특정 임계 값을 초과하지 않을 때 병합합니다. 그렇지 않으면 Quicksort로 폴백 되지만 작은 범위에 대한 삽입 정렬 로 폴백되는 구현이 포함되어 작은 배열뿐만 아니라 빠른 정렬의 재귀에도 영향을줍니다.
  • sort(char[],…)sort(short[],…)다른 특별한 경우를 추가, 사용하는 일종의 계산 배열 길이가 특정 임계 값을 초과에 대한
  • 마찬가지로 Counting sortsort(byte[],…) 를 사용 하지만 Quicksort를 사용하지 않기 때문에 문서와 가장 큰 대조를 이루는 임계 값이 훨씬 더 작습니다 . 작은 배열 에는 삽입 정렬 만 사용 하고 그렇지 않으면 계수 정렬 만 사용합니다 .sort(byte[],…)

1
흠, 흥미롭게도 Collections.sort Javadoc은 "이 정렬은 안정된 것으로 보장됩니다"라고 말합니다.하지만 List.sort에 위임하기 때문에 목록 구현에 의해 재정의 될 수 있기 때문에 모든 목록에 대해 Collections.sort가 안정적인 정렬을 보장 할 수 없습니다. 구현. 아니면 내가 뭔가 놓친 건가요? 그리고 List.sort는 정렬 알고리즘이 안정적 일 필요가 없습니다.
Puce

11
@Puce : 이는 단순히 그 보증에 대한 책임이 이제 우선하는 List.sort방법 을 구현하는 사람들의 손에 있다는 것을 의미합니다 . Collections.sort모든 List구현에 대해 올바른 작업을 보장 할 수 없습니다. 예를 들어 List는 내용을 허위로 변경하지 않습니다. 의 보증은 Collections.sort올바른 List구현 (및 올바른 구현 Comparator또는 equals구현) 에만 적용 된다는 점으로 귀결 됩니다 .
Holger

1
@Puce :하지만 당신은 오른쪽 자바 독은 가장 최근의 문서 상태 두 가지 방법에서 그러나 적어도이 제약에 대해 동등하게 명시하지 있습니다 Collections.sort위임합니다 List.sort.
Holger

@Puce : 중요한 속성이 유형의 일부가 아니라 문서에서만 언급되어 컴파일러에 의해 확인되지 않는 수많은 예제가 있습니다. Java의 유형 시스템은 흥미로운 속성을 표현하기에는 너무 약합니다. (이 점에서 동적으로 입력되는 언어와 크게 다르지 않습니다. 또한 속성이 문서에 정의되어 있으며 위반되지 않았는지 확인하는 것은 프로그래머의 몫입니다.) 실제로 더 나아가 : 눈치 채 셨나요? 그 Collections.sort경우에도 출력이 분류되어 해당 유형 서명에 언급하지 않는 이유는 무엇입니까?
Jörg W Mittag

1
보다 표현적인 유형 시스템을 사용하는 언어에서의 반환 유형은 Collections.sort"1) 입력에있는 모든 요소가 출력에도 존재하는 속성을 가진 입력과 동일한 유형 및 길이의 컬렉션, 2 ) 출력의 모든 요소 쌍에 대해 왼쪽 요소는 오른쪽 요소보다 크지 않습니다. 3) 출력의 모든 동일한 요소 쌍에 대해 왼쪽 요소의 인덱스는 오른쪽 요소보다 작습니다. " 그.
Jörg W Mittag

20

문서에 대해서는 모르지만 java.util.Collections#sortJava 8 (HotSpot) 의 구현 은 다음과 같습니다.

@SuppressWarnings({"unchecked", "rawtypes"})
public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

그리고이 List#sort구현이 있습니다.

@SuppressWarnings({"unchecked", "rawtypes"})
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);
    }
}

따라서 결국 에는 장면 뒤에서 (객체 요소의) Collections#sort사용 Arrays#sort합니다. 이 구현은 병합 정렬 또는 팀 정렬을 사용합니다.


16

Javadoc에 따르면 기본 배열 만 Quicksort를 사용하여 정렬됩니다. 객체 배열도 병합 정렬로 정렬됩니다.

따라서 Collections.sort는 Objects에 대해 Arrays.sort와 동일한 정렬 알고리즘을 사용하는 것 같습니다.

또 다른 질문은 왜 객체 배열보다 원시 배열에 다른 정렬 알고리즘이 사용되는 것입니까?


2

많은 답변에서 언급했듯이.

Quicksort는 안정성이 필요하지 않기 때문에 Arrays.sort에서 기본 컬렉션을 정렬하는 데 사용됩니다 (정렬에서 두 개의 동일한 int가 교체되었는지 알거나 신경 쓰지 않습니다).

MergeSort 또는 더 구체적으로 Timsort는 Arrays.sort에서 개체 컬렉션을 정렬하는 데 사용됩니다. 안정성이 필요합니다. Quicksort는 안정성을 제공하지 않지만 Timsort는 제공합니다.

Collections.sort는 Arrays.sort에 위임하므로 MergeSort를 참조하는 javadoc을 볼 수 있습니다.


1

빠른 정렬에는 병합 정렬과 관련하여 두 가지 주요 단점이 있습니다.

  • 원시적이지 않은 경우 안정적이지 않습니다.
  • n log n 성능을 보장하지 않습니다.

(가치) 평등과 구별되는 동일성 개념이 없기 때문에 안정성은 원시 유형의 경우 문제가되지 않습니다.

안정성은 임의의 개체를 정렬 할 때 큰 문제입니다. Merge Sort가 입력에 관계없이 n log n (시간) 성능을 보장한다는 것은 좋은 부수적 인 이점입니다. 이것이 개체 참조를 정렬하기 위해 안정적인 정렬 (Merge Sort)을 제공하기 위해 병합 정렬을 선택한 이유입니다.


1
"안정적"이란 무슨 뜻입니까?
Arun Gowda
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.