Java LinkedList与ArrayList在维护有序列表方面的性能
我想维护一个大小为的有序Java LinkedList与ArrayList在维护有序列表方面的性能,java,sorting,arraylist,linked-list,Java,Sorting,Arraylist,Linked List,我想维护一个大小为的有序列表,在LinkedList上调用sort()会破坏性能,因为默认实现了List.sort()将列表转换为数组进行排序。在很少的情况下,使用链接列表是有意义的,即使它看起来应该是有效的 如果希望始终对集合进行排序,则确实应该使用有序集合,如TreeSet,甚至可以使用PriorityQueue。它将提供更干净的代码(以及更快的排序),因为您不必一直担心自己调用sort()。您可以使用排序列表上的Collections#binarySearch来找到正确的插入点。Array
列表,在LinkedList
上调用sort()
会破坏性能,因为默认实现了List.sort()
将列表
转换为数组进行排序。在很少的情况下,使用链接列表是有意义的,即使它看起来应该是有效的
如果希望始终对集合进行排序,则确实应该使用有序集合,如TreeSet
,甚至可以使用PriorityQueue
。它将提供更干净的代码(以及更快的排序),因为您不必一直担心自己调用sort()
。您可以使用排序列表上的Collections#binarySearch
来找到正确的插入点。ArrayList可能会比LinkedList性能更好,尤其是对于较大的大小,但这很容易测试
我运行了一个各种方法的微型基准测试:在每次插入后使用sort或二进制搜索插入到正确的位置,使用ArrayList(AL)和LinkedList(LL)。我还添加了Commons树列表和番石榴树列表
结论
- 在被测试的算法中,最好的算法是使用
TreeMultiset
,但严格来说,它不是一个列表-下一个最好的选择是使用ArrayList
+binarySearch
- ArrayList在所有情况下的性能都优于LinkedList,LinkedList需要几分钟才能完成100000个元素(ArrayList只需不到1秒)
最佳执行者代码,供参考:
@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;
}
对于10万件物品:
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
由于java没有内置multiset,这是适合您情况的完美数据结构,因此我建议使用guava库中的
多集允许重复元素,树多集还将增加保持集合排序的好处。在Oracle Java/OpenJDK 7或更高版本下,两者的渐进性能相似Collections.sort
将列表加载到一个数组中,对数组进行排序,然后通过迭代(使用ListIterator
)将数组重新加载到列表中,替换其元素
在这两种情况下,这都是对大部分排序的数组的数组排序(在OpenJDK 7和更高版本中是O(n)
,因为它使用了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<>());
公共类AwfulComparator实现Comparator{
公共整数比较(EO1,EO2){
int比较=o1。与(o2)比较;
返回(比较==0)?1:比较;//从不比较相等
}
}
新树集(新的awfulcomarator());
<>代码> ,如果排序是您的主要性能考虑,您应该考虑使用被设计来维护订单的数据结构。
使用普通Java基类,您可以使用以下任何一种:
PriorityQueue (in case you want to retain duplicates)
TreeSet (filter duplicates)
在任何情况下,对所有版本进行原型化并运行一些基准测试+评测都是最简单的 你已经回答了你的问题,linkedlist显然更有效。另一种选择是像…@Smac89这样的二叉树,我的集合中有重复的元素,所以不能使用集合
,但是类似于二叉搜索树
的东西会很好,因为它比集合的性能更好。sort()
@Smac89为什么您认为它显然更有效?@assylias Well OP确实说过…,但由于我将经常调用sort()方法,我逐渐了解到linkedList在对列表进行排序时会执行得更好,并且将是比ArrayList更好的选择,因为它不会像ArrayList那样移动元素(使用数组作为基础数据结构)。
和ofc,因为对链表进行合并排序是O(nlogn)操作(如果完成)right@Smac89我得到的结果似乎与您的说法不一致。还要注意,在Java中对LinkedList进行排序要比对ArrayList进行排序慢,因为前者意味着将项目的额外副本添加到数组中。我希望列表中的元素重复,因此不能使用Set
和PriorityQueue
只是部分排序,因此这对我的情况也没有帮助。Collections.sort()
将列表转换为数组,然后对其进行排序。因此,您可以有效地使用O(nLogn)
complexity..这是你能得到的最好结果。@MeenaChaudhary那么ArrayList
是你最明智的选择。LinkedList
的性能非常差(几乎在任何情况下都是如此)。合并排序不需要随机访问,可以在O(n log n)中处理链表 >与数组相同。这就是< C++ > />代码>:C++中的排序< <代码> >是“代码> LIKEDLIST。排序< /COD>不是一个专门的合并排序。在OpenJDK上,在执行<代码>集合时,在任何时候都没有。排序< < /代码>是<代码> GET()/Cux>方法。它只使用<代码> toRayAy())/>代码>和<代码> ListItRealTor()。
+1用于二进制搜索:)。但如果OP使用数组列表,则插入后的移位需要时间。在这种情况下,LinkedList
会更有效。我不知道,你可能是对的。binarySearch在LinkedList上的速度可能较慢,但插入速度更快…@LostMind刚刚运行了一些
PriorityQueue (in case you want to retain duplicates)
TreeSet (filter duplicates)