标题说明了一切。我必须将数千个对象添加到列表中,然后对它们进行排序。现在我想(因为向LinkedList添加东西要快得多)我会使用LinkedList进行创建,然后制作一个新的ArrayList,如下所示:
LinkedList<Foo> createList = new LinkedList<Foo>();
// add stuff
ArrayList<Foo> returnList = new ArrayList<Foo>(createList);
Collections.sort(returnList);
return returnList;
我的问题是:这种方法真的比直接将对象添加到ArrayList更快甚至更慢吗?或者,我知道要添加的对象的大致数量。具有初始容量的ArrayList是否更快?
这与两个问题有关:
1。ArrayList
和LinkedList
之间有什么区别,哪一个插入更快
2.哪个排序更快?
对于问题1,ArrayList
和LinkedList
之间的本质区别在于数据结构。ArrayList
使用内部的阵列并且擅长随机接入(O(1))。另一方面,LinkedList
擅长删除和插入项(O(1)。您可以在此处找到更多信息
回到问题,因为我们不需要在此处按索引插入。所以ArrayList
和LinkedList
都是O(1)运算。但由于数据结构的原因,LinkedList
将导致更多的内存,而如果ArrayList
需要扩展容量,则会导致更多的时间(设置足够大的初始容量将有助于加快插入速度)。
对于问题2,你可以在这里找到答案ArrayList
更适合排序。
总之,我认为你应该坚持使用ArrayList,不需要在这里导入LinkedList
。
忘记LinkedList
。它要慢得多,除非您需要像.add(0, item)
或remove(0)
这样的东西,其中ArrayList
具有二次复杂度。只要你只需要.add(item)
和sort
,ArrayList
就会轻而易举地获胜。
另请参阅我的回答。
还要注意,复制到ArrayList
中是有问题的,因为Collections.sort
用于将其自身复制到数组中。从Java8开始,ArrayList
的内部数组直接进行排序。
tl;dr
你问:
创建LinkedList并将其转换为ArrayList进行排序有意义吗?
不,这种转换没有意义。我们在下面的实证基准测试中看到的情况并非如此。
➥ArrayList
和LinkedList
对100000个随机十六进制字符串的列表进行排序所花费的时间大致相同。
小心过早优化
永远不要凭直觉发现性能问题。众所周知,各种技能/经验水平的程序员都不善于发现代码库中的瓶颈。对于高度调优的Java编译器和Java虚拟机实现来说尤其如此。在没有实际证据的情况下担心性能是过早优化的陷阱。
无差异:对100000个元素的ArrayList
或LinkedList
进行排序大约需要0.05秒
做一个快速而肮脏的基准测试表明,实际上您不应该把时间花在ArrayList
与LinkedList
的排序性能上。假设我的基准代码是有效的,那么两个列表的性能几乎相同。我还尝试了SortedSet
的两种实现,TreeSet
和ConcurrentSkipListSet
,因为您说过您的对象集合是不同的(没有重复的对象)。四个集合中的每一个最多需要十分之一秒来填充和排序100000个随机String
元素。
对于超过100000个集合的填充和排序组合,结果约为十分之一秒的一半。由于你的工作只涉及10000人,我们谈论的是一秒钟的极小部分。除非你每秒进行数千/数百万次这样的操作,否则这种填充&列表排序对您的应用程序没有重大影响。
durationArrayList=PT0.038510117S
durationLinkedList=PT0.045435537S
durationTreeSet=PT0.0675355054S
durationConcurrentSkipListSet=PT0.104006783S
这是另一次跑步。
durationArrayList=PT0.031999744S
durationLinkedList=PT0.037321294S
durationTreeSet=PT0.065598175S
durationConcurrentSkipListSet=PT0.084586606S
这些结果来自于在IntelliJ 2020.2内部运行,该软件通过Maven for Java 14编译,使用AdoptOpenJDK(内部版本14.0.1+7),在Mac mini(2018)上运行,该系统配备3 GHz Intel Core i5 CPU,无超线程,32 GB 2667 MHz DDR4,macOS Mojave 10.14.6。
请注意,ConcurrentSkipListSet
占用的时间最长。该类是为并发(线程安全)使用而设计的。因此,如果您需要对集合进行并发保护,那么较长的执行时间是合理的。否则,如果不跨线程访问集合,请改用其他三种集合类型之一。
这是基准代码。请仔细复习,因为我很快就把这个拼在了一起,加上所有的剪切和粘贴,我很可能犯了一个错误。
测试代码调用这个timeout
方法休眠几秒钟,试图让垃圾收集运行(假设JVM受到运行gc循环请求的尊重)。
private void timeout ( )
{
try
{
Thread.sleep( TimeUnit.SECONDS.toMillis( 5 ) );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
}
这是测试代码。
我们从100000个字符串的列表开始,每个字符串都是版本4 UUID的十六进制表示,其中128位中的122位是随机生成的。示例值:0063a1f3-39d0-421b-80ab-70e4e1726904
。
System.out.println( "INFO - Beginning the speed tests for sorting ArrayList, LinkedList, TreeSet, and ConcurrentSkipListSet." );
System.gc();
this.timeout();
// --------| setup | ---------------------
final int LIMIT = 100_000;
List < String > tempList = new ArrayList <>();
for ( int i = 0 ; i < LIMIT ; i++ )
{
tempList.add( UUID.randomUUID().toString() );
}
final List < String > sourceList = List.copyOf( tempList ); // List.copyOf makes an unmodifiable list.
tempList = null ;
{
// Warmup
final List < String > warmUpArrayList = new ArrayList <>( sourceList );
Collections.sort( warmUpArrayList );
final List < String > warmUpLinkedList = new LinkedList <>( sourceList );
Collections.sort( warmUpLinkedList );
final SortedSet < String > warmUpSortedSet = new TreeSet <>( sourceList );
final SortedSet < String > warmUpConcurrentSkipListSet = new ConcurrentSkipListSet <>( sourceList );
}
// Pause
System.gc();
this.timeout();
long start, stop;
// --------| ArrayList | ---------------------
start = System.nanoTime();
List < String > arrayList = new ArrayList <>();
arrayList.addAll( sourceList );
Collections.sort( arrayList );
stop = System.nanoTime();
Duration durationArrayList = Duration.ofNanos( stop - start );
arrayList = null;
// Pause
System.gc();
this.timeout();
// --------| LinkedList | ---------------------
start = System.nanoTime();
List < String > linkedList = new LinkedList <>();
linkedList.addAll( sourceList );
Collections.sort( linkedList );
stop = System.nanoTime();
Duration durationLinkedList = Duration.ofNanos( stop - start );
linkedList = null;
// Pause
System.gc();
this.timeout();
// --------| TreeSet | ---------------------
start = System.nanoTime();
TreeSet < String > treeSet = new TreeSet <>(); // Implements `SorteSet`.
treeSet.addAll( sourceList );
stop = System.nanoTime();
Duration durationTreeSet = Duration.ofNanos( stop - start );
treeSet = null;
// Pause
System.gc();
this.timeout();
// --------| ConcurrentSkipListSet | ---------------------
start = System.nanoTime();
ConcurrentSkipListSet < String > concurrentSkipListSet = new ConcurrentSkipListSet <>(); // Implements `SorteSet`.
concurrentSkipListSet.addAll( sourceList );
stop = System.nanoTime();
Duration durationConcurrentSkipListSet = Duration.ofNanos( stop - start );
concurrentSkipListSet = null;
// Pause
System.gc();
this.timeout();
// --------| reporting | ---------------------
System.out.println( "durationArrayList = " + durationArrayList );
System.out.println( "durationLinkedList = " + durationLinkedList );
System.out.println( "durationTreeSet = " + durationTreeSet );
System.out.println( "durationConcurrentSkipListSet = " + durationConcurrentSkipListSet );