Если у меня Map
такой:
HashMap<Integer, ComparableObject> map;
и я хочу получить набор значений, отсортированных с использованием естественного порядка, какой метод самый быстрый?
(А)
Создайте экземпляр сортируемой коллекции, например ArrayList
, добавьте значения, а затем отсортируйте его:
List<ComparableObject> sortedCollection = new ArrayList<ComparableObject>(map.values());
Collections.sort(sortedCollection);
(В)
Создайте экземпляр упорядоченной коллекции, например TreeSet
, затем добавьте значения:
Set<ComparableObject> sortedCollection = new TreeSet<ComparableObject>(map.values());
Обратите внимание, что результирующая коллекция никогда не изменяется, поэтому сортировка должна выполняться только один раз.
java
sorting
collections
глотать
источник
источник
ComparableObject
), а не по ключу (Integer
).Ответы:
TreeSet имеет гарантию
log(n)
временной сложностиadd()/remove()/contains()
методов. СортировкаArrayList
требуетn*log(n)
операций, ноadd()/get()
занимает только1
операцию.Так что, если вы в основном извлекаете и не сортируете часто,
ArrayList
это лучший выбор. Если вы часто сортируете, но не получаете так много,TreeSet
будет лучшим выбором.источник
ArrayList
это лучший выбор.Теоретически сортировка в конце должна быть быстрее. Поддержание отсортированного состояния в процессе может потребовать дополнительного времени процессора.
С точки зрения CS, обе операции являются NlogN, но 1 сортировка должна иметь меньшую константу.
источник
Почему бы не использовать лучшее из обоих миров? Если вы больше никогда не будете его использовать, отсортируйте с помощью TreeSet и инициализируйте ArrayList с содержимым
List<ComparableObject> sortedCollection = new ArrayList<ComparableObject>( new TreeSet<ComparableObject>(map.values()));
РЕДАКТИРОВАТЬ:
Я создал тест (вы можете получить к нему доступ на pastebin.com/5pyPMJav ), чтобы протестировать три подхода (ArrayList + Collections.sort, TreeSet и мой лучший подход из обоих миров), и мой всегда выигрывает. В тестовом файле создается карта с 10000 элементами, значения которых имеют намеренно неправильный компаратор, а затем каждая из трех стратегий получает возможность а) отсортировать данные и б) перебрать их. Вот пример вывода (вы можете проверить это сами):
РЕДАКТИРОВАТЬ: Я добавил аспект, который регистрирует вызовы Thingy.compareTo (Thingy), и я также добавил новую стратегию на основе PriorityQueues, которая намного быстрее, чем любое из предыдущих решений (по крайней мере, при сортировке).
compareTo() calls:123490 Transformer ArrayListTransformer Creation: 255885873 ns (0.255885873 seconds) Iteration: 2582591 ns (0.002582591 seconds) Item count: 10000 compareTo() calls:121665 Transformer TreeSetTransformer Creation: 199893004 ns (0.199893004 seconds) Iteration: 4848242 ns (0.004848242 seconds) Item count: 10000 compareTo() calls:121665 Transformer BestOfBothWorldsTransformer Creation: 216952504 ns (0.216952504 seconds) Iteration: 1604604 ns (0.001604604 seconds) Item count: 10000 compareTo() calls:18819 Transformer PriorityQueueTransformer Creation: 35119198 ns (0.035119198 seconds) Iteration: 2803639 ns (0.002803639 seconds) Item count: 10000
Как ни странно, мой подход лучше всего работает в итерации (я бы подумал, что в итерации не будет отличий от подхода ArrayList, есть ли у меня ошибка в моем тесте?)
Отказ от ответственности: я знаю, что это, вероятно, ужасный тест, но он помогает донести до вас суть, и я, конечно же, не манипулировал им, чтобы мой подход победил.
(Код имеет зависимость от apache commons / lang для разработчиков equals / hashcode / compareTo, но его должно быть легко реорганизовать)
источник
new TreeSet<ComparableObject>(map.values())
возвращается. Обертывание этого в файлеArrayList
просто добавит ненужных операций.Collection
... чтоTreeSet
есть. Я не вижу смысла преобразовывать набор в список здесь.Transformer
экземпляры, которые находятся позже в списке, быстрее, чем более ранние: поставьтеBestOfBothWorldsTransformer
первым, и он внезапно работает намного медленнее. Итак, я переписал ваш тест, чтобы случайным образом выбрать трансформатор и усреднить результаты. В моем тестеTreeSetTransformer
стабильно бьетBestOfBothWorldsTransformer
, который стабильно бьетArrayListTransformer
- совсем не то, что я ожидал! Однако разница небольшая. См pastebin.com/L0t5QDV9PriorityQueue
неправильно пользуюсь ? У вас есть пример правильной сортировки?Обязательно прочтите мой комментарий о TreeSet внизу, если вы решите реализовать B)
Если ваше приложение выполняет только случайные сортировки, но часто повторяет его, я бы сказал, что вам лучше использовать простой несортированный список. Отсортируйте его один раз, а затем воспользуйтесь более быстрой итерацией. Итерация особенно быстро выполняется в списке массивов.
Однако, если вы хотите, чтобы порядок сортировки гарантировался все время или вы, возможно, часто добавляете / удаляете элементы, используйте отсортированную коллекцию и принимайте удар при итерации.
Так что в вашем случае я бы сказал, что A) - лучший вариант. Список сортируется один раз, не изменяется, поэтому он может быть массивом. Итерация должна быть очень быстрой, особенно если вы знаете, что это ArrayList, и можете напрямую использовать ArrayList.get () вместо Iterator.
Я бы также добавил, что TreeSet по определению является Set, что означает, что объекты уникальны. TreeSet определяет равенство, используя compareTo в вашем Comparator / Comparable. Вы можете легко обнаружить, что у вас отсутствуют данные, если попытаетесь добавить два объекта, для которых compareTo возвращает значение 0. например, добавление «C», «A», «B», «A» в TreeSet вернет «A», «B» "," C "
источник
TreeSet
потенциально отсутствующих данных, если compareTo возвращает 0. Я определил, что в этом конкретном случае реализация compareTo никогда не вернет 0, поэтому обаTreeSet
иArrayList
будут вести себя одинаково. Однако я и раньше сталкивался с этой проблемой, поэтому спасибо за напоминание!PriorityQueue
действительно работает быстрее, но когда я попробовал, значения на самом деле не были отсортированы - очевидно, почему это было так быстро! Возможно, я неправильно понял, как использовать PriorityQueue ... был бы полезен пример того, как это действительно работает.Collections.sort
использует mergeSort с O (nlog n).TreeSet
имеет красно-черное дерево, базовые операции имеют O (logn). Следовательно, n элементов также имеет O (nlog n).Таким образом, оба являются одним и тем же алгоритмом большого О.
источник
Вставка в SortedSet - это O (log (n)) (НО! Текущее n, а не последнее n). Вставка в список - 1.
Сортировка в SortedSet уже включена во вставку, поэтому она равна 0. Сортировка в списке - O (n * log (n)).
Таким образом, общая сложность SortedSet составляет O (n * k), k <log (n) для всех случаев, кроме последнего. Вместо этого общая сложность списка равна O (n * log (n) + n), поэтому O (n * log (n)).
Итак, SortedSet математически имеет лучшую производительность. Но, в конце концов, у вас есть Set вместо List (потому что SortedList не существует), а Set предоставляет вам меньше возможностей, чем List. Поэтому, на мой взгляд, лучшее решение с точки зрения доступных функций и производительности - это решение, предложенное Шоном Патриком Флойдом:
источник
Отличный вопрос и отличные ответы. Просто подумал, что я бы добавил несколько моментов, которые нужно учесть:
Обоснование: отсортированная коллекция требуется для чего-то конкретного, и вы, вероятно, не будете часто добавлять или удалять. Таким образом, вы действительно не заботитесь об элементах в коллекции после ее сортировки. Вы в основном:
сортировать -> использовать -> забыть
Если вы добавите новый элемент в отсортированную коллекцию, вам придется снова отсортировать коллекцию, поскольку порядок при вставке нового элемента не гарантируется.
Обоснование: Вы всегда заботитесь о порядке инкассации. Вы хотите, чтобы это всегда было отсортировано. Поэтому, если вы постоянно добавляете или удаляете элементы, у вас есть гарантия, что коллекция отсортирована. Итак, в основном:
вставить / удалить -> использовать (всегда есть гарантия, что коллекция отсортирована)
Нет определенного момента, когда вам нужно сортировать коллекцию, вместо этого вы хотите, чтобы коллекция сортировалась постоянно.
Обратной стороной использования TreeSet являются ресурсы, необходимые для хранения отсортированной коллекции. Он использует красно-черное дерево и требует затрат времени O (log n) для операций get, put.
Если вы используете простую коллекцию, такую как ArrayList, операции get, add имеют постоянное время O (1).
источник