Я хочу сохранить упорядоченный List<Integer>
размера <= 10 ^ 6. Каждый раз, когда будет добавлен новый элемент, я вызову метод Collections.sort()
для сортировки нового элемента в списке. По моим знаниям ArrayList
лучше, чем LinkedList
. Но так как я часто вызываю метод sort()
, я понял, что LinkedList
будет работать лучше при сортировке списка и будет лучшим выбором по сравнению с ArrayList
, так как нет смещения элементов, как в случае ArrayList
(использует array
в качестве базовой структуры данных). Любые предложения, которые будут более эффективными.
Производительность LinkedList vs ArrayList при сохранении упорядоченного списка
Ответ 1
Вы можете использовать Collections#binarySearch
в отсортированном списке, чтобы найти правильную точку вставки. ArrayList, вероятно, будет работать лучше, чем LinkedList, особенно для больших размеров, но это легко проверить.
Я запускал микро-тест различных методов: используя сортировку после каждой вставки или бинарный поиск для вставки в нужном месте, как с ArrayList (AL), так и с LinkedList (LL). Я также добавил Commons TreeList и guava TreeMultiset.
Выводы
- лучший из тех, кто тестировал, использует
TreeMultiset
, но он не является списком строго говоря. Следующим лучшим вариантом является использованиеArrayList
+ binarySearch - ArrayList работает лучше, чем LinkedList во всех ситуациях, и последний занял несколько минут, чтобы заполнить 100 000 элементов (ArrayList занял менее одной секунды).
Код лучшего исполнителя, для справки:
@Benchmark public ArrayList<Integer> binarySearchAL() {
ArrayList<Integer> list = new ArrayList<> ();
Random r = new Random();
for (int i = 0; i < n; i++) {
int num = r.nextInt();
int index = Collections.binarySearch(list, num);
if (index >= 0) list.add(index, num);
else list.add(-index - 1, num);
current = list.get(0); //O(1), to make sure the sort is not optimised away
}
return list;
}
Полный код на bitbucket.
Полные результаты
В столбце "Бенчмарк" содержится имя тестируемого метода (baseLine просто заполняет список без его сортировки, другие методы имеют явные имена: AL = ArrayList, LL = LinkedList, TL = Commons TreeList, treeMultiSet = guava), (n) - размер списка, Score - время, затраченное миллисекундами.
Benchmark (n) Mode Samples Score Error Units
c.a.p.SO28164665.baseLine 100 avgt 10 0.002 ± 0.000 ms/op
c.a.p.SO28164665.baseLine 1000 avgt 10 0.017 ± 0.001 ms/op
c.a.p.SO28164665.baseLine 5000 avgt 10 0.086 ± 0.002 ms/op
c.a.p.SO28164665.baseLine 10000 avgt 10 0.175 ± 0.007 ms/op
c.a.p.SO28164665.binarySearchAL 100 avgt 10 0.014 ± 0.001 ms/op
c.a.p.SO28164665.binarySearchAL 1000 avgt 10 0.226 ± 0.006 ms/op
c.a.p.SO28164665.binarySearchAL 5000 avgt 10 2.413 ± 0.125 ms/op
c.a.p.SO28164665.binarySearchAL 10000 avgt 10 8.478 ± 0.523 ms/op
c.a.p.SO28164665.binarySearchLL 100 avgt 10 0.031 ± 0.000 ms/op
c.a.p.SO28164665.binarySearchLL 1000 avgt 10 3.876 ± 0.100 ms/op
c.a.p.SO28164665.binarySearchLL 5000 avgt 10 263.717 ± 6.852 ms/op
c.a.p.SO28164665.binarySearchLL 10000 avgt 10 843.436 ± 33.265 ms/op
c.a.p.SO28164665.sortAL 100 avgt 10 0.051 ± 0.002 ms/op
c.a.p.SO28164665.sortAL 1000 avgt 10 3.381 ± 0.189 ms/op
c.a.p.SO28164665.sortAL 5000 avgt 10 118.882 ± 22.030 ms/op
c.a.p.SO28164665.sortAL 10000 avgt 10 511.668 ± 171.453 ms/op
c.a.p.SO28164665.sortLL 100 avgt 10 0.082 ± 0.002 ms/op
c.a.p.SO28164665.sortLL 1000 avgt 10 13.045 ± 0.460 ms/op
c.a.p.SO28164665.sortLL 5000 avgt 10 642.593 ± 188.044 ms/op
c.a.p.SO28164665.sortLL 10000 avgt 10 1182.698 ± 159.468 ms/op
c.a.p.SO28164665.binarySearchTL 100 avgt 10 0.056 ± 0.002 ms/op
c.a.p.SO28164665.binarySearchTL 1000 avgt 10 1.083 ± 0.052 ms/op
c.a.p.SO28164665.binarySearchTL 5000 avgt 10 8.246 ± 0.329 ms/op
c.a.p.SO28164665.binarySearchTL 10000 avgt 10 735.192 ± 56.071 ms/op
c.a.p.SO28164665.treeMultiSet 100 avgt 10 0.021 ± 0.001 ms/op
c.a.p.SO28164665.treeMultiSet 1000 avgt 10 0.288 ± 0.008 ms/op
c.a.p.SO28164665.treeMultiSet 5000 avgt 10 1.809 ± 0.061 ms/op
c.a.p.SO28164665.treeMultiSet 10000 avgt 10 4.283 ± 0.214 ms/op
За 100 тыс. элементов:
c.a.p.SO28164665.binarySearchAL 100000 avgt 6 890.585 ± 68.730 ms/op
c.a.p.SO28164665.treeMultiSet 100000 avgt 6 105.273 ± 9.309 ms/op
Ответ 2
Поскольку java не имеет встроенного мультимножества, что является идеальной структурой данных для вашей ситуации, я предлагаю использовать TreeMultiset найденный в библиотеке guava.
Multisets позволяют дублировать элементы, а мультимножество дерева также добавит преимущества сохранения сортировки вашей коллекции.
Ответ 3
Вызов sort()
в LinkedList
является разрушительным для производительности, из-за реализации List.sort()
по умолчанию преобразования List
в массив для сортировки. Очень мало случаев, когда имеет смысл использовать LinkedList
, хотя может показаться, что он должен быть эффективным.
Если вы хотите, чтобы коллекция всегда сортировалась, вы действительно должны использовать упорядоченную коллекцию, например, TreeSet
или, возможно, даже PriorityQueue
. Он будет обеспечивать более чистый код (а также более быструю сортировку), так как вам не нужно беспокоиться о вызове sort()
самостоятельно все время.
Ответ 4
В Oracle Java/OpenJDK 7 или выше асимптотическая производительность обоих будет одинаковой. Collections.sort
загружает список в массив, сортирует массив и загружает массив обратно в список, итерации через него (используя ListIterator
), заменяя его элементы.
В обоих случаях это сортировка массива в основном отсортированном массиве (который является O(n)
в OpenJDK 7 и выше, поскольку он использует timsort), плюс две итерации списка (которые в O(n)
в обоих случаях - хотя я бы ожидал, что LinkedList
будет иметь худший постоянный термин). В общем, это процесс O(n)
, но, скорее всего, медленнее для LinkedList
.
Если вы являетесь объемными элементами вставки, объемная вставка будет O(n^2)
в целом, что медленнее, чем вставлять их все и сортировать, или после предложения Smac89
использования TreeMultiset
(оба будут O(n log(n))
).
И просто для удовольствия, здесь действительно ужасный способ злоупотребления TreeSet
, чтобы он мог хранить повторяющиеся элементы:
public class AwfulComparator<E extends Comparable<E>> implements Comparator<E> {
public int compare(E o1, E o2) {
int compared = o1.compareTo(o2);
return (compared == 0)?1:compared; // Never compare equal
}
}
new TreeSet<String>(new AwfulComparator<>());
Ответ 5
Вам следует подумать о том, чтобы использовать структуры данных, которые предназначены для поддержания порядка, если сортировка является вашей главной оценкой производительности.
Используя обычные базовые классы Java, вы можете использовать любой из них:
PriorityQueue (in case you want to retain duplicates)
TreeSet (filter duplicates)
В любом случае будет проще всего прототипировать все версии и запустить некоторые тесты + профилирование.