Java에 SortedList가없는 이유는 무엇입니까?


502

Java에는 SortedSetSortedMap인터페이스가 있습니다. 둘 다 Java Collections 프레임 워크 에 속하며 요소에 액세스하는 정렬 된 방법을 제공합니다.

그러나 내 이해에는 SortedListJava 가 없습니다 . java.util.Collections.sort()목록을 정렬하는 데 사용할 수 있습니다 .

왜 그런 식으로 설계되었는지 아십니까?



6
목록 중간에 요소를 삽입 할 때 예상되는 결과는 무엇입니까?
bestsss

4
@bestsss java.util.List 인터페이스를 구현하지 않는 SortedList 클래스를 가질 수 있습니다. 요청 된 기능을 지원하는 데이터 구조가없는 이유를 질문으로 읽으십시오. 네이밍과 같은 중요하지 않은 세부 사항에주의를 분산시키지 마십시오.
Alderath

@Alderath의 일반적인 구조는 순서를 지원하기 위해 여분의 이전 / 다음 링크가있는 트리 (빨강 / 검정, avl 또는 btree)입니다. 비슷한 구조의 빨강 / 검정 / 이전 / 다음 링크를 사용합니다. 그래도 꽤 틈새 사용입니다. 트리는 순서와 삽입 순서 모두를 순회 할 수 있으며, O (logn) 찾기 / 포함하지만 get (int)는 O (n)입니다. 틈새 적용 가능성을 감안할 때 개발자가 필요한 경우 구현해야한다고 생각합니다.
bestsss

3
"why"라는 질문에 대답하지는 않지만 TreeSet 의 가장 간단한 해결 방법 은 0을 반환하지 않는 Comparator를 사용하는 int diff = this.score - that.score; return (diff == 0) ? 1 : diff; 것입니다.
earcam

답변:


682

리스트 반복자는 가장 먼저리스트의 내부 순서 (일명 삽입 순서 ) 로리스트의 요소를 얻는다는 것을 보장 합니다. 더 구체적으로 말하면 요소를 삽입 한 순서 또는 목록을 조작 한 방법입니다. 정렬은 데이터 구조를 조작하는 것으로 볼 수 있으며 목록을 정렬하는 몇 가지 방법이 있습니다.

개인적으로 볼 때 유용한 순서대로 방법을 주문 합니다.

1. 대신 Set또는 사용을 고려하십시오.Bag

참고 : 이 옵션은 맨 위에 놓았습니다. 일반적으로하고 싶은 일이기 때문입니다.

정렬 된 세트 는 삽입시 컬렉션을 자동으로 정렬합니다 . 즉, 컬렉션에 요소를 추가하는 동안 정렬을 수행합니다. 또한 수동으로 정렬 할 필요가 없습니다.

또한 중복 요소에 대해 걱정할 필요가 없거나 TreeSet<T>대신 요소를 사용할 필요가 없다면 대신 사용할 수 있습니다 . 구현 SortedSet하고 NavigableSet인터페이스하며 목록에서 예상 한대로 작동합니다.

TreeSet<String> set = new TreeSet<String>();
set.add("lol");
set.add("cat");
// automatically sorts natural order when adding

for (String s : set) {
    System.out.println(s);
}
// Prints out "cat" and "lol"

자연스러운 순서를 원하지 않으면을 사용하는 생성자 매개 변수를 사용할 수 있습니다 Comparator<T>.

또한, 당신이 사용할 수있는 멀티 세트를 (라고도 가방 ) , 그건입니다 Set대신에 중복 요소를 허용하는 그들의 타사 구현이있다. 가장 주목할만한로부터 구아바 라이브러리TreeMultiset등 많은 작품을, TreeSet.

2. 목록을 정렬하십시오 Collections.sort()

위에서 언급했듯이 Lists 정렬은 데이터 구조의 조작입니다. 따라서 다양한 방법으로 정렬되는 "한 가지 진실의 원천"이 필요한 상황에서는 수동으로 정렬하는 것이 좋습니다.

java.util.Collections.sort()방법으로 목록을 정렬 할 수 있습니다 . 방법에 대한 코드 샘플은 다음과 같습니다.

List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");

Collections.sort(strings);
for (String s : strings) {
    System.out.println(s);
}
// Prints out "cat" and "lol"

비교기 사용

한 가지 분명한 이점은 당신이 사용할 수 있다는 것이다 Comparatorsort방법. Java는 로케일 구분 정렬 문자열에 유용한 Comparator것과 같은 일부 구현도 제공합니다 Collator. 다음은 하나의 예입니다.

Collator usCollator = Collator.getInstance(Locale.US);
usCollator.setStrength(Collator.PRIMARY); // ignores casing

Collections.sort(strings, usCollator);

동시 환경에서 정렬

sort컬렉션 인스턴스가 조작되므로 변경 불가능한 컬렉션 사용을 고려해야하므로 동시 사용 환경에서는 이 방법 을 사용하는 것이 쉽지 않습니다. 이것은 구아바가 Ordering수업 에서 제공 하는 것으로 간단한 원 라이너입니다.

List<string> sorted = Ordering.natural().sortedCopy(strings);

3. 당신의 목록을 포장 java.util.PriorityQueue

Java에는 정렬 된 목록이 없지만 정렬 대기열은 아마 당신에게 잘 작동 할 것입니다. 그것은이다 java.util.PriorityQueue클래스입니다.

Nico Haase는 의견 에이 답변에 관한 관련 질문에 연결했습니다 .

정렬 된 컬렉션 에서 내부 데이터 구조 를 조작하지 않으려는 경우가 많으 므로 PriorityQueue가 List 인터페이스를 구현하지 않는 이유는 요소에 직접 액세스 할 수 있기 때문입니다.

PriorityQueue반복자 에 대한 경고

PriorityQueue클래스의 구현 Iterable<E>Collection<E>는 평소와 같이 반복 할 수 있도록 인터페이스를 제공합니다. 그러나 이터레이터는 정렬 된 순서로 요소를 반환한다고 보장하지 않습니다. 대신 (Alderath가 주석에서 지적한 것처럼) poll()비어있을 때까지 대기열에 있어야합니다.

당신이를 통해 우선 순위 큐에 목록을 변환 할 수 있습니다 어떤 수집을 소요 생성자 :

List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");

PriorityQueue<String> sortedStrings = new PriorityQueue(strings);
while(!sortedStrings.isEmpty()) {
    System.out.println(sortedStrings.poll());
}
// Prints out "cat" and "lol"

4. 자신의 SortedList수업을 작성하십시오

참고 : 이 작업을 수행하지 않아도됩니다.

새 요소를 추가 할 때마다 정렬되는 고유 한 List 클래스를 작성할 수 있습니다. 이것은 구현에 따라 오히려 계산 무거운를 얻을 수 있습니다 및 무의미 하기 때문에 두 가지 이유, 당신이 연습으로 그것을하지 않으려면 :

  1. 메소드가 요소가 사용자가 지정한 색인에 상주하는지 확인해야하기 List<E>때문에 인터페이스가 갖는 계약을 위반 add합니다.
  2. 왜 바퀴를 재발 명합니까? 위의 첫 번째 지점에서 지적한대로 TreeSet 또는 Multisets를 대신 사용해야합니다.

그러나 연습으로 사용하려면 시작하기위한 코드 샘플이 있으며 AbstractList추상 클래스를 사용합니다 .

public class SortedList<E> extends AbstractList<E> {

    private ArrayList<E> internalList = new ArrayList<E>();

    // Note that add(E e) in AbstractList is calling this one
    @Override 
    public void add(int position, E e) {
        internalList.add(e);
        Collections.sort(internalList, null);
    }

    @Override
    public E get(int i) {
        return internalList.get(i);
    }

    @Override
    public int size() {
        return internalList.size();
    }

}

필요한 메소드를 재정의하지 않은 경우 기본 구현은 AbstractList을 던집니다 UnsupportedOperationException.


12
+1. 이모, 이것은 최고 투표 답변보다 건설적인 것입니다. 그러나 두 가지 사소한 단점이 있습니다. PriorityQueue는 임의 액세스를 지원하지 않습니다. peek (elementIndex)를 수행 할 수 없습니다. 따라서 당신은 예를 들어 할 수 없습니다 Integer maxVal = prioQueue.peek(prioQueue.size() - 1);. 둘째, PriorityQueue를 단순히 정렬 된 목록으로 사용하려는 경우 이러한 데이터 구조가 존재하는 경우 PriorityQueue코드에서 보는 것보다 직관적으로 들리지 않습니다 SortedList.
Alderath

12
그리고 누군가가 의견에 링크 된 다른 질문을 살펴본 후 PriorityQueue 반복자가 특정 순서로 요소를 반환한다고 보장 할 수 없다는 또 다른 큰 단점 이 있습니다. 따라서 무언가를 간과하지 않는 한 PriorityQueue의 모든 객체를 인쇄하는 유일한 방법은 대기열이 비워 질 때까지 반복적으로 poll ()하는 것입니다. 나에게,이 경계선은 retarted 느낀다. PriorityQueue의 객체를 두 번 인쇄하려면 우선 PriorityQueue를 복사 한 다음 원래 PriorityQueue의 모든 객체를 poll () 한 다음 사본의 모든 객체를 pol () l해야합니다.
Alderath

1
흠 ... 네가 맞은 것 같아. 당신은 예상 순서로 요소를 얻을 수 PriorityQueue 인의 반복자를 사용할 수 없습니다. 답변을 수정해야 할 것 같습니다.
Spoike

7
우선 순위 대기열은 힙에 불과하며 맨 위에 만 액세스 할 수 있습니다.
bestsss

1
또한 가치 지적이 있다는 것입니다 Collections.sort()심지어 당신이 사용하여 정렬에 사용되는 비교 함수를 정의 할 수 있습니다 Comparator개체를.
Mike 'Pomax'Kamermans

71

List의 개념은 자동 정렬 된 컬렉션의 개념과 호환되지 않기 때문입니다. List의 요점은 호출 한 후에 list.add(7, elem)대한 호출 list.get(7)이 반환 elem됩니다. 자동 정렬 목록을 사용하면 요소가 임의의 위치에있게됩니다.


26

항목이 추가 된 순서 (FIFO 순서)에 따라 모든 목록이 이미 "정렬"되었으므로을 사용하여 요소의 자연 순서를 포함하여 다른 순서로 항목을 "정렬"할 수 있습니다 java.util.Collections.sort().

편집하다:

데이터 구조 등의 목록은 항목이 삽입되는 순서입니다 흥미로운에 근거하고 있습니다.

세트에는 해당 정보가 없습니다.

시간을 추가하여 주문하려면을 사용하십시오 List. 다른 기준을 사용하여 주문합니다 SortedSet.


22
세트는 복제를 허용하지 않습니다
bestsss

10
나는 이것이 매우 좋은 대답이라고 생각하지 않습니다. 물론, Java API의 목록에는 항목 삽입시기 / 방법에 따라 결정되는 특정 순서가 있습니다. 그러나, 삽입 방법 / 시간에 의존하는 방식으로 정렬 된리스트의 존재는 다른 방식으로 (예를 들어, 비교기에 의해) 순서가 결정되는 다른 데이터 구조를 갖는 것을 방지하지 못한다. 기본적으로 OP는 데이터 구조가 동일한 요소의 여러 발생을 허용해야한다는 점을 제외하고 SortedSet과 동등한 데이터 구조가없는 이유를 묻습니다.
Alderath

5
질문까지 내 후속이 될 것이다 그래서 " 왜 여러 개의 동일한 요소를 포함 할 수있는 SortedSet의처럼 작동 데이터 구조는 없지만,? (그리고"대답하지 마십시오 " 설정은 하나 개의 요소를 포함 할 수 있기 때문에 ")
Alderath

@Alderath : stackoverflow.com/questions/416266/sorted-collection-in-java를 참조하십시오 . 한마디로 : 사용 구아바의 TreeMultiset
야누스 Troelsen

4
목록 당신이 따옴표에 "분류"넣어 경우에도, "분류"NOT된다. 그들은 주문했다.
Koray Tugay

20

설정 및지도 비선형 데이터 구조입니다. 리스트는 선형 데이터 구조입니다.

여기에 이미지 설명을 입력하십시오


트리 데이터 구조 SortedSetSortedMap인터페이스 구현을 TreeSet하고 TreeMap각각 사용하여 레드 - 블랙 트리 구현 알고리즘을. 따라서 중복 항목 (또는 경우 키)이 없는지 확인합니다 Map.

  • List 이미 정렬 된 컬렉션 및 인덱스 기반 데이터 구조를 유지하고 있으며 트리는 인덱스 기반 데이터 구조가 아닙니다.
  • Tree 정의상 중복은 포함 할 수 없습니다.
  • 에서 List우리는 중복을 가질 수 있습니다, 그래서이 없다 TreeList(NO 즉 SortedList).
  • List는 요소를 삽입 순서대로 유지합니다. 따라서 목록을 정렬하려면을 사용해야 java.util.Collections.sort()합니다. 요소의 자연 순서에 따라 지정된 목록을 오름차순으로 정렬합니다.

14

JavaFX SortedList

시간이 오래 걸렸지 만 Java 8에는 정렬되어 List있습니다. http://docs.oracle.com/javase/8/javafx/api/javafx/collections/transformation/SortedList.html

javadocs에서 볼 수 있듯이 이는 JavaFX 콜렉션의 일부이며 ObservableList에 대해 정렬 된보기를 제공하기위한 것입니다.

업데이트 : Java 11에서는 JavaFX 툴킷이 JDK 외부로 이동했으며 이제 별도의 라이브러리입니다. JavaFX 11은 다운로드 가능한 SDK 또는 MavenCentral에서 제공됩니다. 참조 https://openjfx.io를


2
불행하게도,이 SortedList는 보통의 목록과 같은 작업은하지 않습니다 - 예를 들어, 기본 생성자 (당신이 ... ObservableList, 그것이 의미하는 것은 무엇을 사용하여 구성 할 필요)가없는
Erel 시걸-Halevi

JavaFX의 SortedList는 GUI 구성 요소를 사용하기위한 것이며 정렬 된 개체 목록만으로는 적합하지 않은 오버 헤드로 인해 명확합니다. 또한 그것은 GUI 프로젝트에 사용되지 않은 경우에도 전체 FX 모듈을 참조하는 것을 의미한다.
COBRA.cH

1
@ COBRA.cH 그렇습니다. 성능이 높은 정렬 목록은 아마도 TreeMap 주위의 얇은 래퍼 일 수 있습니다. 여기서 정수는 키의 중복을 계산하는 데 사용됩니다. 0을 반환하지 않는 비교기와 함께 TreeSet을 사용할 수도 있습니다.
swpalmer

다운 투표를하는 이유는 무엇입니까? 문제는 JDK 라이브러리에 정렬 된 목록이 없다는 전제로 시작합니다. 이 답변은 그 가정을 수정합니다. 이 특정 정렬 목록의 구현을 좋아하든 해제하든 투표를하지 않아도됩니다. 이 답변은 클래스를 권장 하지 않으며 단지 클래스의 존재를 지적합니다.
Basil Bourque

10

2015 년 4 월 현재 모든 신규 사용자를 위해 Android 는 지원 라이브러리에으로 작업하도록 특별히 설계된 SortedList 클래스를 제공합니다 RecyclerView. 여기 블로그 게시물 이 있습니다.


1
이 의견을 제출할 당시 Androids SortedList에는 RecyclerView와 함께 onItemMoved () 기능이 지원되지 않습니다. 효율성이 떨어지는 내 자신의 SortedList를 작성하는 것은 한계를 극복하기 위해해야했던 일입니다.
Matthew

4

또 다른 요점은 삽입 작업의 시간 복잡성입니다. 목록 삽입의 경우 O (1)의 복잡성을 예상합니다. 그러나 정렬 된 목록으로는 보장 할 수 없습니다.

그리고 가장 중요한 점은 목록이 해당 요소에 대해 아무 것도 가정하지 않는다는 것입니다. 예를 들어, equals또는 구현하지 않은 것들의 목록을 만들 수 있습니다 compare.


O (logn) 삽입 / 삭제 / 찾기 / 포함하지만 get (int) / 포함하지 않은 목록을 가질 수 있습니다.
bestsss

3
마지막 요점은 실제로 좋은 설명이 아닙니다. SortedSet구현하지 않은 것을 만들 수 있습니다 Comparable. 이 TreeSet 생성자를 참조하십시오 .
Alderath

1
@Alderath-어쩌면 내 말이 너무 약했을 수 있습니다. 그러나 목차의 요소와 세트의 요소는 적어도 동등성을 위해 비교 가능해야하지만 목록 요소는 필요하지 않습니다. 세트와 트리의 순서 / 평등 관계가 Comparator 또는 다른 곳에서 구현되는지 여부는 중요하지 않지만 필요합니다.
Ingo

목록 O (1) 삽입을 보장 하지 않으며 O (1) 액세스를 보장 합니다 . bigocheatsheet.com
Matt Quigley 4

1
@Betlista 당신이 맞아요! 내 의견을 업데이트 할 수는 없지만 Java List인터페이스는 메소드의 성능 사양을 보장하지 않습니다.
Matt Quigley

3

다음과 같이 생각하십시오. List인터페이스에는 add(int index, E element), 와 같은 메소드가 있습니다 set(int index, E element). 계약은 X 위치에 요소를 추가 한 후에는 요소를 추가하거나 제거하지 않는 한 해당 요소를 찾을 수 있다는 것입니다.

목록 구현이 색인을 기준으로하는 것 이외의 순서로 요소를 저장하는 경우 위의 목록 방법은 의미가 없습니다.


2

List API의 첫 번째 줄은 순서 컬렉션이라고도합니다. 목록을 정렬하면 순서를 유지할 수 없으므로 Java에는 TreeList가 없습니다.
API가 말한 것처럼 Java List는 Sequence에서 영감을 얻었으며 시퀀스 속성을 참조하십시오 http://en.wikipedia.org/wiki/Sequence_(mathematics )

그것은 당신이 목록을 정렬 할 수 없다는 것을 의미하지는 않지만 Java는 그의 정의에 엄격하며 기본적으로 정렬 된 버전의 목록을 제공하지 않습니다.



2

요소를 정렬하는 방법을 찾고 있지만 효율적인 방법으로 색인을 통해 요소에 액세스 할 수있는 경우 다음을 수행 할 수 있습니다.

  1. 저장을위한 랜덤 액세스 목록을 사용하여 (예를 들어 ArrayList)
  2. 항상 정렬되어 있는지 확인하십시오

그런 다음 요소를 추가하거나 제거 Collections.binarySearch하기 위해 삽입 / 제거 색인을 얻는 데 사용할 수 있습니다 . 목록이 임의 액세스를 구현하므로 결정된 색인으로 목록을 효율적으로 수정할 수 있습니다.

예:

/**
 * @deprecated
 *      Only for demonstration purposes. Implementation is incomplete and does not 
 *      handle invalid arguments.
 */
@Deprecated
public class SortingList<E extends Comparable<E>> {
    private ArrayList<E> delegate;

    public SortingList() {
        delegate = new ArrayList<>();
    }

    public void add(E e) {
        int insertionIndex = Collections.binarySearch(delegate, e);

        // < 0 if element is not in the list, see Collections.binarySearch
        if (insertionIndex < 0) {
            insertionIndex = -(insertionIndex + 1);
        }
        else {
            // Insertion index is index of existing element, to add new element 
            // behind it increase index
            insertionIndex++;
        }

        delegate.add(insertionIndex, e);
    }

    public void remove(E e) {
        int index = Collections.binarySearch(delegate, e);
        delegate.remove(index);
    }

    public E get(int index) {
        return delegate.get(index);
    }
}

1

indexed-tree-map 사용을 고려하십시오 . 인덱스로 요소에 액세스하고 트리를 백업하는 숨겨진 기본 목록이나 반복없이 요소의 인덱스를 찾는 향상된 JDK의 TreeSet입니다. 이 알고리즘은 변경이있을 때마다 변경 노드의 가중치 업데이트를 기반으로합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.