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

161

Я новичок в Java. Пожалуйста, предложите, какую коллекцию (ы) можно / нужно использовать для поддержания отсортированного списка в Java. Я пытался Mapи Set, но они были не то, что я искал.

b4hand
источник

Ответы:

188

Это приходит очень поздно, но в JDK есть класс только для того, чтобы иметь отсортированный список. Он назван (несколько не в порядке с другими Sorted*интерфейсами) " java.util.PriorityQueue". Он может сортировать Comparable<?>s или используя Comparator.

Разница с использованием Listотсортированного использования Collections.sort(...)заключается в том, что он всегда будет поддерживать частичный порядок с эффективностью вставки O (log (n)), используя структуру данных кучи, тогда как вставка в отсортированный ArrayListбудет O (n) (т.е. используя бинарный поиск и перемещение).

Однако, в отличие от a List, PriorityQueueне поддерживает индексированный доступ ( get(5)), единственный способ получить доступ к элементам в куче - вынимать их по одному (таким образом, имя PriorityQueue).

Мартин Пробст
источник
4
ИМХО этот ответ заслуживает большего числа голосов, поскольку он указывает на единственную коллекцию, которая имеет возможности в JDK
nimcap
96
Из Javadoc: «Итератор, предоставленный в методе iterator (), не гарантирует прохождение элементов PriorityQueue в любом конкретном порядке».
Кристофер Хаммарстрем
10
@giraff: приоритетная очередь - это просто структура данных, которая очень эффективно хранит приоритетную очередь. Вы можете опросить с фронта и получить элементы данных в отсортированном порядке. Однако кучи не поддерживают общий порядок элементов внутри (именно поэтому они так эффективны), поэтому нет способа получить доступ к элементам по порядку без выполнения операции poll.
Мартин Пробст
2
@ chrispy, это зависит от того, чего именно вы хотите достичь с помощью вашей структуры данных. Кучи - это эффективный способ поддерживать список элементов и впоследствии получать их упорядоченным образом - использование итератора не работает, но если вы выполните опрос, вы получите данные в порядке. Извлечение является разрушительным, но во многих случаях это может быть хорошо. Поэтому я думаю, что этот ответ хороший.
Мартин Пробст
4
@MartinProbst Пожалуйста, исправьте свой ответ в CLEARLY и укажите, что эта коллекция НЕ МОЖЕТ ИТЕРИРОВАТЬСЯ в ожидаемом порядке. Как уже говорили многие, это чрезвычайно вводит в заблуждение!
Хорхе Гальвао
53

TreeMap и TreeSet дадут вам итерацию по содержимому в отсортированном порядке. Или вы можете использовать ArrayList и использовать Collections.sort () для его сортировки. Все эти классы находятся в java.util

Майкл Боргвардт
источник
27
Однако есть два основных недостатка, первый из которых заключается в том, что в наборе не может быть дубликатов. Во-вторых, если вы используете list и Collections.sort (), вы, как правило, постоянно сортируете огромные списки и получаете низкую производительность. Конечно, вы можете использовать «грязный» флаг, но это не совсем то же самое.
Jeach
Это ставит вопрос о том, есть ли у нас опция в Java, которая позволяет дублировать, а также дает производительность, аналогичную Set (сбалансированному двоичному дереву)
mankadnandan
32

Если вы хотите сохранить отсортированный список, который вы будете часто изменять (т.е. структуру, которая, помимо сортировки, допускает дублирование и на элементы которой можно эффективно ссылаться по индексу), то используйте ArrayList, но когда вам нужно вставить элемент Всегда используйте Collections.binarySearch () для определения индекса, по которому вы добавляете данный элемент. Последний метод сообщает вам индекс, который нужно вставить, чтобы сохранить список в отсортированном порядке.

Нил Коффи
источник
11
n вставок будет O (n ^ 2). TreeSet даст вам более чистый код и O (n log n). Ото, для нечастого поиска модификации двоичного массива будет быстрее и использовать меньше памяти (тем меньше накладные расходы ГХ).
Том Хотин - tackline
Чтобы сохранить код в чистоте и по-прежнему учитывать дубликаты, было бы достаточно легко создать класс SortedList, который вставляет значения в отсортированном порядке.
Мистер Блестящий и Новый
Это намного лучший ответ, чем ответ с наибольшим количеством голосов, imo, если вы можете жить с оговоркой, упомянутой @ TomHawtin-tackline. То, что итератор работает должным образом, имеет решающее значение для большинства случаев.
DuneCat
Кстати, Том прав в этом конкретном поведении: набор деревьев даст вам более эффективную модификацию. Но набор деревьев не является списком (в самом строгом смысле, когда элементы имеют ссылки на индексы и допускаются дубликаты), и автор сказал, что ему нужен список.
Нил Коффи
Спасибо, Нил, это было чертовски круто!
Викингстеве
31

Используйте класс GoogleMava TreeMultiset . Guava имеет впечатляющий API коллекций.

Одной из проблем, связанных с предоставлением реализации List, которая поддерживает отсортированный порядок, является обещание, данное в JavaDocs add()метода.

JTB
источник
Слава за предложение мультимножества
darthtrevino
5
+1 за упоминание требования, которое Listвсегда добавляет в конце.
Роланд Иллиг
2
Обратите внимание, что TreeMultiSet по-прежнему не допускает дублирования элементов (элементы, которые compareTo () возвращает 0, а не equals ()). Если вы склонны добавлять более одного элемента с одинаковым приоритетом, это только увеличит количество первого добавленного элемента, отбрасывая другой и фактически являясь хорошей реализацией Counting Bag.
2012 г.
12

Вы хотите реализации SortedSet , а именно TreeSet .

Клетус
источник
10
Не обязательно; наборы не могут иметь повторяющиеся значения. Это зависит от требований ОП.
Зак Лэнгли
12

Есть несколько вариантов. Я бы предложил TreeSet, если вам не нужны дубликаты, а вставляемые объекты сравнимы.

Вы также можете использовать статические методы класса Collections для этого.

См. Collections # sort (java.util.List) и TreeSet для получения дополнительной информации.

BenM
источник
10

Если вы просто хотите отсортировать список, используйте любой вид List и используйте 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);
}

После этого я реализовал новый метод "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, потому что они могут изменить ваш внутренний список. В этом случае вы можете вернуть копию внутреннего списка или снова выдать исключение.

public ListIterator<Contact> listIterator() {
    return (new ArrayList<Contact>(list)).listIterator();
}
Карлос Вердес
источник
Проблема в том, что это нарушает Listдоговор. Может быть, было бы лучше только реализовать Collection. И если ContactListотсортировано, то contains()может быть реализовано с использованием binarySearchтакже, чтобы быть более эффективным.
Marcono1234
5

Самый эффективный способ реализовать отсортированный список, как вы хотите, - это реализовать индексируемый скип-лист, как здесь: Wikipedia: Индексируемый скип-лист . Это позволило бы иметь вставки / удаления в O (log (n)) и позволило бы иметь индексированный доступ одновременно. И это также позволило бы дубликаты.

Skiplist - довольно интересная и, я бы сказал, недооцененная структура данных. К сожалению, в базовой библиотеке Java нет индексированной реализации списка пропусков, но вы можете использовать одну из реализаций с открытым исходным кодом или реализовать ее самостоятельно. Существуют регулярные реализации Skiplist, такие как ConcurrentSkipListSet и ConcurrentSkipListMap

vladich
источник
4

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

Giuseppe
источник
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] и queue [2 * (n + 1)]". Это прекрасно работает, если вы просто тянете с головы, но делает его бесполезным, если вы пытаетесь вызвать .toArray для него в какой-то момент.

Я обхожу эту проблему, используя com.google.common.collect.TreeMultimap, но я поставляю собственный компаратор для значений, заключенных в упорядочение, который никогда не возвращает 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, этот класс должен реализовать сопоставимый интерфейс, который, в свою очередь, заставляет реализовать метод сравнения с (), который содержит определение порядка сортировки. http://techmastertutorial.in/java-collection-impl.html

Ави Тяги
источник
0

Используйте метод sort () для сортировки списка, как показано ниже:

List list = new ArrayList();

//add elements to the list

Comparator comparator = new SomeComparator();

Collections.sort(list, comparator);

Для справки смотрите ссылку: http://tutorials.jenkov.com/java-collections/sorting.html

Шашанк Мунгантивар
источник
0

Использование, TreeSetкоторое дает элементы в отсортированном порядке. ИЛИ использовать Collection.sort()для внешней сортировки с Comparator().

Хари
источник
TreeSet не допускает дублирование элементов. Иногда это желаемая особенность, другие нет. Учитывая, что ОП попробовал наборы, я предполагаю, что нет
usr-local-ΕΨΗΕΛΩΝ
Не позорьте
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
skpaik
источник