Java에서 정렬 된 컬렉션


161

저는 Java 초보자입니다. Java에서 정렬 된 목록을 유지하는 데 사용할 수있는 모음을 제안하십시오. 나는 시도 Map하고 Set, 그러나 그들은 내가 찾던하지 무엇을했다.

답변:


187

이것은 매우 늦지 만 JDK에는 정렬 된 목록을 가질 목적으로 클래스가 있습니다. 이름이 (다른 Sorted*인터페이스 와 순서가 맞지 않음) " java.util.PriorityQueue"입니다. 을 Comparable<?>사용 하거나을 사용하여 정렬 할 수 있습니다 Comparator.

List정렬 된 사용법 과의 차이점 Collections.sort(...)은 힙 데이터 구조를 사용하여 O (log (n)) 삽입 성능을 사용하여 항상 부분 순서를 유지하지만 정렬 된 삽입은 ArrayListO (n)입니다 (즉, 이진 검색 및 이동 사용).

그러나, 달리 List, PriorityQueue인덱스 액세스 (지원하지 않습니다 get(5)), 한 번에 힙이 그들을 걸릴 것입니다에 항목에 액세스 할 수있는 유일한 방법은, 하나의 (따라서 이름을 PriorityQueue).


4
imo이 답변은 JDK 기능을 가진 유일한 컬렉션을 지적하기 때문에 더 많은지지를받을 가치가 있습니다
nimcap

96
Javadoc에서 : "iterator () 메소드에서 제공되는 Iterator는 PriorityQueue의 요소를 특정 순서로 순회한다고 보장하지 않습니다."
Christoffer Hammarström

10
@giraff : 우선 순위 큐는 우선 순위 큐를 유지하는 데 매우 효율적인 데이터 구조입니다. 앞에서 폴링하고 데이터 항목을 정렬 된 순서로 가져올 수 있습니다. 그러나 힙은 내부에서 전체 요소 순서를 유지하지 않으므로 (그래서 매우 효율적이기 때문에) 폴링 조작을 실행하지 않고 요소에 순서대로 액세스 할 수있는 방법이 없습니다.
Martin Probst

2
@ chrispy 그것은 데이터 구조로 정확히 달성하고자하는 것에 약간 달려 있습니다. 힙은 항목 목록을 유지 관리하고 나중에 순서대로 정렬 할 수있는 효율적인 방법입니다. 반복자를 사용하는 것은 작동하지 않지만 폴링하면 데이터를 순서대로 가져옵니다. 검색은 파괴적이지만 많은 경우에 여전히 괜찮습니다. 그래서 나는이 대답이 좋다고 생각합니다.
Martin Probst 2016 년

4
@MartinProbst 해당 컬렉션이 예상 순서대로 수행 될 수 없음을 명확하게 표시하려면 답변을 수정하십시오. 많은 사람들이 말했듯이, 이것은 매우 오해의 소지가 있습니다!
Jorge Galvão

53

TreeMap과 TreeSet은 내용을 정렬 된 순서로 반복합니다. 또는 ArrayList를 사용하고 Collections.sort ()를 사용하여 정렬 할 수 있습니다. 모든 클래스는 java.util에 있습니다.


27
그러나 이것에는 두 가지 주요 단점이 있습니다. 첫 번째는 Set이 복제본을 가질 수 없다는 것입니다. 두 번째는 목록과 Collections.sort ()를 사용하면 지속적으로 큰 목록을 정렬하는 경향이 있으며 성능이 저하됩니다. 물론 '더러운'플래그를 사용할 수는 있지만 완전히 다릅니다.
Jeach

그것은 우리가 dupliactes는 또한 세트 (균형 이진 트리)와 유사한 성능을 제공 할 수 있습니다 자바의 옵션이 수행 여부를 질문에 가져다
mankadnandan

32

자주 수정 하는 정렬 된 목록 을 유지 하려면 (즉, 정렬 외에도 중복을 허용하고 인덱스로 요소를 효율적으로 참조 할 수있는 구조), ArrayList를 사용하지만 요소를 삽입해야 할 때 지정된 요소를 추가 할 색인을 결정하려면 항상 Collections.binarySearch ()를 사용하십시오 . 후자의 방법은 목록을 정렬 된 순서로 유지하기 위해 삽입해야하는 색인을 알려줍니다.


11
n 인서트는 O (n ^ 2)입니다. TreeSet은 더 깨끗한 코드와 O (n log n)를 제공합니다. 자주 수정 하지 않는 OTOH의 경우, 이진 검색은 더 빠르며 메모리를 덜 사용합니다 (따라서 GC 오버 헤드가 줄어 듭니다).
Tom Hawtin-tackline

코드를 깨끗하게 유지하고 중복을 허용하려면 값을 정렬 된 순서로 삽입하는 SortedList 클래스를 만드는 것이 쉽습니다.
Mr. Shiny and New 安 宇

@ TomHawtin-tackline이 언급 한주의 사항에 따라 살 수 있다면 가장 많이 찬 대답 인 imo보다 더 나은 방법입니다. 반복자가 예상대로 작동하는 것은 대부분의 경우 중요합니다.
DuneCat

덧붙여서, Tom은 그 특정 행동에 맞습니다. 트리 세트는 더 효율적인 수정을 제공합니다. 그러나 트리 세트는 목록이 아니며 (인덱스로 요소를 참조하고 중복을 허용하는 가장 엄격한 의미에서) 목록을 원한다고 포스터는 말했습니다.
Neil Coffey

닐, 고마워
vikingsteve

31

Google Guava의 TreeMultiset 클래스를 사용하십시오 . 구아바 에는 화려한 컬렉션 API가 있습니다.

정렬 순서를 유지하는 List 구현을 제공하는 데있어 한 가지 문제점은 add()메소드 의 JavaDoc에서 작성된 약속 입니다.


다중 집합 제안을위한 Kudos
darthtrevino

5
List끝에 항상 추가 해야하는 요구 사항을 언급하면 ​​+1입니다 .
Roland Illig

2
TreeMultiSet은 여전히 ​​중복 요소 (compatTo ()가 equals () 검사가 아닌 0을 반환하는 요소)를 허용하지 않습니다. 우선 순위가 같은 항목을 두 개 이상 추가하려는 경우 처음 추가 한 항목의 수만 증가시켜 다른 항목은 버리고 효과적으로 Counting Bag 구현이됩니다.
bekce


12

몇 가지 옵션이 있습니다. 중복을 원하지 않고 삽입하는 객체가 비슷한 경우 TreeSet을 제안합니다.

Collections 클래스의 정적 메소드를 사용하여이를 수행 할 수도 있습니다.

자세한 정보는 Collections # sort (java.util.List)TreeSet 를 참조하십시오.



5

내가 한 것은 위임 된 모든 메소드가있는 내부 인스턴스가있는 List 구현입니다.

 public class ContactList implements List<Contact>, Serializable {
    private static final long serialVersionUID = -1862666454644475565L;
    private final List<Contact> list;

public ContactList() {
    super();
    this.list = new ArrayList<Contact>();
}

public ContactList(List<Contact> list) {
    super();
    //copy and order list
    List<Contact>aux= new ArrayList(list);
    Collections.sort(aux);

    this.list = aux;
}

public void clear() {
    list.clear();
}

public boolean contains(Object object) {
    return list.contains(object);
}

그 후, 요소가 존재하지 않거나 요소가 존재하는 경우를 대비하여 올바른 위치에 삽입하는 새로운 메소드 "putOrdered"를 구현했습니다.

public void putOrdered(Contact contact) {
    int index=Collections.binarySearch(this.list,contact);
    if(index<0){
        index= -(index+1);
        list.add(index, contact);
    }else{
        list.set(index, contact);
    }
}

반복되는 요소를 허용하려면 addOrdered를 대신 구현하십시오 (또는 둘 다).

public void addOrdered(Contact contact) {
    int index=Collections.binarySearch(this.list,contact);
    if(index<0){
        index= -(index+1);
    }
    list.add(index, contact);
}

삽입을 피하려면 "add"및 "set"메소드에서 지원되지 않는 조작 예외를 처리 할 수 ​​있습니다.

public boolean add(Contact object) {
    throw new UnsupportedOperationException("Use putOrdered instead");
}

... 또한 ListIterator 메소드는 내부 목록을 수정할 수 있으므로주의해야합니다. 이 경우 내부 목록의 복사본을 반환하거나 예외를 다시 throw 할 수 있습니다.

public ListIterator<Contact> listIterator() {
    return (new ArrayList<Contact>(list)).listIterator();
}

문제는 이것이 List계약을 위반한다는 것 입니다. 어쩌면 구현하는 것이 낫습니다 Collection. 만약 ContactList정렬 한 후 contains()사용하여 구현할 수 있습니다 binarySearch더 효율적으로뿐만 아니라.
Marcono1234

5

원하는 정렬 목록을 구현하는 가장 효율적인 방법은 Wikipedia : Indexable skiplist 와 같이 인덱싱 가능한 스킵 목록을 구현하는 것 입니다. O (log (n))에 삽입 / 제거를 허용하고 동시에 인덱스 액세스를 허용합니다. 또한 중복을 허용합니다.

스킵리스트는 꽤 흥미롭고 저평가 된 데이터 구조입니다. 불행히도 Java 기본 라이브러리에는 인덱싱 된 건너 뛰기 목록이 없지만 오픈 소스 구현 중 하나를 사용하거나 직접 구현할 수 있습니다. ConcurrentSkipListSetConcurrentSkipListMap 과 같은 일반적인 Skiplist 구현이 있습니다.


4

TreeSet은 중복을 허용하지 않기 때문에 작동하지 않으며 특정 위치에서 요소를 가져 오는 메소드를 제공하지 않습니다. PriorityQueue는 목록의 기본 요구 사항 인 특정 위치에서 요소를 페치 할 수 없으므로 작동하지 않습니다. 중복이 필요하지 않은 경우 O (logn) 삽입 시간으로 Java에서 정렬 된 목록을 유지하려면 자체 알고리즘을 구현해야한다고 생각합니다. 아마도 솔루션은 키가 equals 메서드를 재정 의하여 항목의 하위 클래스 인 TreeMap을 사용하여 복제가 허용 될 수 있습니다.


4

LambdaJ 사용

Java 8 이전 버전을 사용하는 경우 LambdaJ로 이러한 작업을 해결할 수 있습니다. http://code.google.com/p/lambdaj/

여기 예가 있습니다 :

반복 정렬

List<Person> sortedByAgePersons = new ArrayList<Person>(persons);
Collections.sort(sortedByAgePersons, new Comparator<Person>() {
        public int compare(Person p1, Person p2) {
           return Integer.valueOf(p1.getAge()).compareTo(p2.getAge());
        }
}); 

LambdaJ로 정렬

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 

물론 이런 종류의 아름다움이 성능에 영향을 미치지 만 (평균 2 배) 더 읽기 쉬운 코드를 찾을 수 있습니까?

람다 식을 사용하여 Java 8로 정렬

Collections.sort(persons, (p1, p2) -> p1.getAge().compareTo(p2.getAge()));
//or
persons.sort((p1, p2) -> p1.getAge().compareTo(p2.getAge()));

Java 8 람다 식을 사용한 마지막 예제에서 어떻게 역 정렬 할 수 있습니까?
Rajat Shah

1
@RajatShah 난 그냥 당신이 할 수있을 것 같아요-(p1.getAge().compareTo(p2.getAge()))
Federico Piazza

@RajatShah 기꺼이 도와주십시오
Federico Piazza

2

PriorityQueue의 문제점은 간단한 배열에 의해 백업되며 요소를 순서대로 가져 오는 논리는 "queue [2 * n + 1] 및 queue [2 * (n + 1)]"에 의해 수행된다는 것입니다. 그냥 머리에서 당기면 효과적이지만 어떤 시점에서 .toArray를 호출하려고하면 쓸모가 없습니다.

com.google.common.collect.TreeMultimap을 사용하여이 문제를 해결하지만 Ordering에 래핑 된 값에 대한 사용자 지정 비교기를 제공하여 0을 반환하지 않습니다.

전의. 더블 용 :

private static final Ordering<Double> NoEqualOrder = Ordering.from(new Comparator<Double>() {

    @Override
    public int compare(Double d1, Double d2)
    {
        if (d1 < d2) {
            return -1;
        }
        else {
            return 1;
        }
    }
});

이 방법으로 .toArray ()를 호출 할 때 값을 순서대로 가져오고 중복도 있습니다.


1

원하는 것은 이진 검색 트리입니다. 검색, 제거 및 삽입에 대한 로그 액세스를 제공하면서 정렬 된 순서를 유지합니다 (트리가 변성되지 않은 경우 선형 임). 구현하기가 쉽고 List 인터페이스를 구현할 수도 있지만 인덱스 액세스가 복잡해집니다.

두 번째 방법은 ArrayList와 버블 정렬 구현을 갖는 것입니다. 한 번에 하나의 요소를 삽입하거나 제거하기 때문에 삽입 및 제거에 대한 액세스 시간은 선형입니다. 검색은 로그 및 인덱스 액세스 상수입니다 (LinkedList의 경우 시간이 다를 수 있음). 필요한 유일한 코드는 5, 6 줄의 거품 정렬입니다.


1

반복 값을 원한다고 말했듯이 Arraylist와 Treemap을 사용할 수 있지만 TreeSet도 정렬 할 수는 없지만 비교자를 정의해야합니다.


1

Set에는 TreeSet을 사용할 수 있습니다. TreeSet은 자연 순서 또는 특정 개체에 대해 Comparable에 전달 된 정렬 순서에 따라 요소의 순서를 지정합니다. 맵의 경우 TreeMap을 사용하십시오. TreeMap은 키 정렬을 제공합니다. TreeMap에 객체를 키로 추가하려면 해당 클래스는 비슷한 인터페이스를 구현해야하며,이 인터페이스는 정렬 순서의 정의를 포함하는 compare to () 메서드를 강제로 구현해야합니다. http://techmastertutorial.in/java-collection-impl.html



0

를 사용하여 TreeSet정렬 된 순서로 요소를 제공합니다. 또는을 사용 Collection.sort()하여 외부 정렬에 사용하십시오 Comparator().


TreeSet은 요소 복제를 허용하지 않습니다. 때로는 원하는 기능이지만 그렇지 않은 경우도 있습니다. 나는 노에 대한 생각, 영업 이익이 세트를 시도 고려
USR-로컬 ΕΨΗΕΛΩΝ

그러지 말고 TreeMap도 지적하십시오. docs.oracle.com/javase/10/docs/api/java/util/TreeMap.html
Haakon Løtveit

0
import java.util.TreeSet;

public class Ass3 {
    TreeSet<String>str=new TreeSet<String>();
    str.add("dog");
    str.add("doonkey");
    str.add("rat");
    str.add("rabbit");
    str.add("elephant");
    System.out.println(str);    
}

0

Java 8 Comparator를 사용하여 목록을 정렬하려면 세계에서 가장 인구가 많은 10 개 도시가 있으며 Time에서보고 한 것처럼 이름별로 정렬하려고합니다. 일본 오사카. 멕시코 시티. ... 중국 베이징. 브라질 상파울루 인도 뭄바이 ... 중국 상하이. ... 인도 델리. ... 도쿄, 일본.

 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;

public class SortCityList {

    /*
     * Here are the 10 most populated cities in the world and we want to sort it by
     * name, as reported by Time. Osaka, Japan. ... Mexico City, Mexico. ...
     * Beijing, China. ... São Paulo, Brazil. ... Mumbai, India. ... Shanghai,
     * China. ... Delhi, India. ... Tokyo, Japan.
     */
    public static void main(String[] args) {
        List<String> cities = Arrays.asList("Osaka", "Mexico City", "São Paulo", "Mumbai", "Shanghai", "Delhi",
                "Tokyo");
        System.out.println("Before Sorting List is:-");
        System.out.println(cities);
        System.out.println("--------------------------------");

        System.out.println("After Use of List sort(String.CASE_INSENSITIVE_ORDER) & Sorting List is:-");
        cities.sort(String.CASE_INSENSITIVE_ORDER);
        System.out.println(cities);
        System.out.println("--------------------------------");
        System.out.println("After Use of List sort(Comparator.naturalOrder()) & Sorting List is:-");
        cities.sort(Comparator.naturalOrder());
        System.out.println(cities);

    }

}

0

사용자 정의 기준에 따라 ArrayList를 정렬합니다.

모델 클래스

 class Student 
 { 
     int rollno; 
     String name, address; 

     public Student(int rollno, String name, String address) 
     { 
         this.rollno = rollno; 
         this.name = name; 
         this.address = address; 
     }   

     public String toString() 
     { 
         return this.rollno + " " + this.name + " " + this.address; 
     } 
 } 

정렬 클래스

 class Sortbyroll implements Comparator<Student> 
 {         
     public int compare(Student a, Student b) 
     { 
         return a.rollno - b.rollno; 
     } 
 } 

메인 클래스

 class Main 
 { 
     public static void main (String[] args) 
     { 
         ArrayList<Student> ar = new ArrayList<Student>(); 
         ar.add(new Student(111, "bbbb", "london")); 
         ar.add(new Student(131, "aaaa", "nyc")); 
         ar.add(new Student(121, "cccc", "jaipur")); 

         System.out.println("Unsorted"); 
         for (int i=0; i<ar.size(); i++) 
             System.out.println(ar.get(i)); 

         Collections.sort(ar, new Sortbyroll()); 

         System.out.println("\nSorted by rollno"); 
         for (int i=0; i<ar.size(); i++) 
             System.out.println(ar.get(i)); 
     } 
 } 

산출

 Unsorted
 111 bbbb london
 131 aaaa nyc
 121 cccc jaipur

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