Я хочу сохранить упорядоченный 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)
В любом случае будет проще всего прототипировать все версии и запустить некоторые тесты + профилирование.