Сортирована колекція на Java


161

Я початківець в Java. Будь ласка, підкажіть, які колекції можуть / повинні використовуватися для ведення відсортованого списку на Java. Я спробував Mapі Set, але вони були не те , що я шукав.

Відповіді:


187

Це відбувається дуже пізно, але в JDK є клас лише для того, щоб мати відсортований список. Він названий (дещо не в порядку з іншими Sorted*інтерфейсами) " java.util.PriorityQueue". Він може сортувати або Comparable<?>s, або використовуючи Comparator.

Відмінність Listвідсортованого використання Collections.sort(...)полягає в тому, що це підтримуватиме частковий порядок у будь-який час, з продуктивністю вставки O (log (n)), використовуючи структуру даних купи, тоді як впорядкована вставка ArrayListбуде O (n) (тобто за допомогою двійкового пошуку та переміщення).

Однак, на відміну від a List, PriorityQueueне підтримує індексований доступ ( get(5)), єдиний спосіб доступу до елементів у купі - це виймати їх по одному (таким чином, назва PriorityQueue).


4
imo ця відповідь заслуговує на більшу кількість відгуків, оскільки вона вказує на єдину колекцію, яка має можливість в JDK
nimcap

96
З Javadoc: "Ітератор, що надається в методі iterator (), не гарантує переміщення елементів PriorityQueue в будь-якому конкретному порядку."
Крістофер Хаммарстрем

10
@giraff: черговістю пріоритетів є саме це, структура даних, яка є дуже ефективною при збереженні черги з пріоритетом. Ви можете опитувати з фронту і отримувати елементи даних у відсортованому порядку. Однак купи не підтримують внутрішній загальний порядок елементів (тому вони настільки ефективні), тому немає можливості отримати доступ до елементів, щоб не виконати опитування.
Мартін Пробст

2
@chrispy це трохи залежить від того, що ви точно хочете досягти зі своєю структурою даних. Купи - це ефективний спосіб підтримувати список предметів та отримувати їх впорядкованим способом пізніше - використання ітератора не працює, але якщо ви опитуєтесь з нього, ви отримаєте свої дані в порядку. Пошук є руйнівним, але це все ще може бути нормальним у багатьох випадках. Тому я вважаю, що ця відповідь хороша.
Мартін Пробст

4
@MartinProbst Будь ласка, виправте свою відповідь на ЧИСТО, вкажіть, що ця колекція НЕ МОЖЕ ІТЕРАТУРАТИ в очікуваному порядку. Як багато хто казав, це надзвичайно вводить в оману!
Хорхе Гальвао

53

TreeMap і TreeSet нададуть вам ітерацію вмісту в упорядкованому порядку. Або ви можете використовувати ArrayList і використовувати Collections.sort () для сортування. Усі ці заняття в java.util


27
Однак у цьому є два основні недоліки, перший з яких полягає в тому, що набір не може мати дублікатів. Друга полягає в тому, що якщо ви використовуєте список та Collections.sort (), ви, як правило, постійно сортуєте величезні списки та даєте низьку продуктивність. Звичайно, ви можете використовувати "брудний" прапор, але це не зовсім те саме.
Jeach

Це приводить до питання, чи є у нас варіант на Java, який дозволяє дублікати, а також давати вистави, схожі на Set (Balanced Binary Tree)
mankadnandan

32

Якщо ви хочете підтримувати відсортований список, який ви будете часто змінювати (тобто структуру, яка, крім сортування, дозволяє копії та елементи, на які можна ефективно посилатись за індексом), тоді використовуйте ArrayList, але коли вам потрібно вставити елемент , завжди використовуйте Collections.binarySearch (), щоб визначити індекс, до якого ви додаєте заданий елемент. Останній метод повідомляє вам індекс, який потрібно вставити, щоб зберегти список у відсортованому порядку.


11
n вставок буде O (n ^ 2). TreeSet надасть вам чистіший код та O (n log n). OTOH, для рідкісних модифікацій двійковий пошук масиву буде швидшим і використовуватиме менше пам'яті (тому менше накладних витрат GC).
Том Хотін - тайклін

Щоб зберегти код чистим і все ще допускати дублікати, було б досить просто створити клас SortedList, який вставляє значення в упорядкованому порядку.
Містер Блискучий та Новий 安 宇

Це спосіб кращої відповіді, ніж відповідь, що найбільше підтримує, імо, якщо ви можете жити з застереженням, згаданим @ TomHawtin-tackline. Щоб ітератор працював, як очікувалося, вирішальне для більшості випадків.
DuneCat

До речі, Том має рацію щодо конкретної поведінки: набір дерев дасть вам більш ефективну модифікацію. Але набір дерев не є списком (в найсуворішому значенні, щоб елементи посилалися на індекс та дозволяли дублікати), а плакат сказав, що хоче список.
Ніл Коффі

Дякую Ніл, це було криваво!
vikingsteve

31

Використовуйте клас TreeMultiset Google Guava . У Guava є ефектна колекція API.

Однією із проблем із забезпеченням реалізації списку, який підтримує відсортований порядок, є обіцянки, зроблені в JavaDocs add()методу.


Кудо за пропозицію мультисетів
дарттревіно

5
+1 для згадування вимоги, яку Listзавжди додається в кінці.
Roland Illig

2
Зауважте, що TreeMultiSet як і раніше не дозволяє повторювати елементи (елементи, які порівнюютьTo () повертають 0, не дорівнює ()). Якщо ви схильні додавати більше одного пункту з одним пріоритетом, це лише збільшить кількість перших доданих, відкинувши інших, і фактично буде хорошою програмою для підрахунку сумки.
bekce


12

Є кілька варіантів. Я б запропонував TreeSet, якщо ви не хочете копій, а об'єкти, які ви вставляєте, порівнянні.

Для цього також можна використовувати статичні методи класу Collections.

Додаткову інформацію див. У колекціях # sort (java.util.List) та TreeSet .


10

Якщо ви просто хочете сортувати список, використовуйте будь-який тип списку та використовуйте Collections.sort () . Якщо ви хочете, щоб елементи у списку були унікальними та завжди відсортованими, використовуйте SortedSet .


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);
}

Після цього я реалізував новий метод "putOrряд", який вставляють у правильне положення, якщо елемент не існує або заміняється на всякий випадок, якщо він існує.

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);
    }
}

Якщо ви хочете дозволити повторювані елементи, просто застосуйте addOrряд замість (або обидва).

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

Якщо ви хочете уникнути вставок, ви можете також викидати і непідтримувані винятки операцій методами "додати" та "встановити".

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

... а також ви повинні бути обережними з методами ListIterator, оскільки вони можуть змінити ваш внутрішній список. У цьому випадку ви можете повернути копію внутрішнього списку або знову кинути виняток.

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

Проблема в тому, що це порушує Listдоговір. Можливо, краще було б лише здійснити Collection. А якщо ContactListвідсортовано, то це contains()може бути реалізовано binarySearchі з використанням більш ефективної.
Marcono1234

5

Найефективнішим способом реалізації відсортованого списку, як ви хочете, було б застосувати індексируваний пропускний список, як тут: Вікіпедія: Індекс, що індексується . Це дозволило б мати вставки / видалення в O (log (n)) і дозволило б одночасно мати індексований доступ. І це також дозволило б дублікати.

Skiplist - це досить цікава і, я б сказав, недооцінена структура даних. На жаль, в базовій бібліотеці Java немає індексованої реалізації пропускних списків, але ви можете використовувати одну з реалізацій з відкритим кодом або реалізувати її самостійно. Є регулярні реалізації Skiplist, такі як ConcurrentSkipListSet та ConcurrentSkipListMap


4

TreeSet не працює, тому що вони не дозволяють дублікати плюс вони не надають метод для отримання елемента в певній позиції. PriorityQueue не працюватиме, оскільки не дозволяє отримати елементи в певній позиції, що є основною вимогою для списку. Я думаю, що вам потрібно реалізувати власний алгоритм для підтримки відсортованого списку в Java з вкладеним часом O (logn), якщо вам не потрібні дублікати. Можливо, рішенням може бути використання TreeMap, де ключ є підкласом елемента, що переосмислює метод рівних, так що дублікати дозволені.


4

Використання LambdaJ

Ви можете спробувати вирішити ці завдання з LambdaJ, якщо ви використовуєте попередні версії до java 8. Ви можете знайти їх тут: 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 лямбда, як можна зробити один зворотний сортування?
Раджат Шах

1
@RajatShah Я думаю, що можна просто зробити-(p1.getAge().compareTo(p2.getAge()))
Федеріко П'яцца

@RajatShah радий допомогти
Федеріко П'яцца

2

Проблема з PriorityQueue полягає в тому, що він підкріплений простим масивом, а логіка, яка наводить елементи в порядку, виконується "queue [2 * n + 1] and queue [2 * (n + 1)]" thingie. Це чудово працює, якщо ви просто тягнете з голови, але робить його марним, якщо ви намагаєтесь в якийсь момент викликати .toArray на ньому.

Я подолаю цю проблему, використовуючи com.google.common.collect.TreeMultimap, але надаю користувальницький компаратор для значень, загорнутих у замовлення, який ніколи не повертає 0.

колишній для Double:

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 замовляє його елементи на основі природного впорядкування або будь-якого порядку сортування, переданого на Порівняльний для цього конкретного об'єкта. Для карти використовуйте TreeMap. TreeMap забезпечує сортування клавіш. Щоб додати об’єкт як ключ до TreeMap, цей клас повинен реалізувати порівнянний інтерфейс, який, у свою чергу, змушує реалізувати метод порівняння з (), який містить визначення порядку сортування. http://techmastertutorial.in/java-collection-impl.html



0

Використовуйте, TreeSetщо дає елементи в упорядкованому порядку. АБО використовувати Collection.sort()для зовнішнього сортування за допомогою Comparator().


TreeSet не дозволяє дублювати елементи. Іноді це бажана особливість, інші - ні. Враховуючи, що ОП випробував набори, я думаю, що для no
usr-local-ΕΨΗΕΛΩΝ

Не майте на увазі, вкажіть і на 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, якщо ми хочемо сортувати список, то ось 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.