Java:创建LinkedList并将其转换为ArrayList进行排序有意义吗



标题说明了一切。我必须将数千个对象添加到列表中,然后对它们进行排序。现在我想(因为向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。ArrayListLinkedList之间有什么区别,哪一个插入更快
2.哪个排序更快?

对于问题1,ArrayListLinkedList之间的本质区别在于数据结构。ArrayList使用内部的阵列并且擅长随机接入(O(1))。另一方面,LinkedList擅长删除和插入项(O(1)。您可以在此处找到更多信息
回到问题,因为我们不需要在此处按索引插入。所以ArrayListLinkedList都是O(1)运算。但由于数据结构的原因,LinkedList将导致更多的内存,而如果ArrayList需要扩展容量,则会导致更多的时间(设置足够大的初始容量将有助于加快插入速度)。

对于问题2,你可以在这里找到答案ArrayList更适合排序。

总之,我认为你应该坚持使用ArrayList,不需要在这里导入LinkedList

忘记LinkedList。它要慢得多,除非您需要像.add(0, item)remove(0)这样的东西,其中ArrayList具有二次复杂度。只要你只需要.add(item)sortArrayList就会轻而易举地获胜。

另请参阅我的回答。


还要注意,复制到ArrayList中是有问题的,因为Collections.sort用于将其自身复制到数组中。从Java8开始,ArrayList的内部数组直接进行排序。

tl;dr

你问:

创建LinkedList并将其转换为ArrayList进行排序有意义吗?

不,这种转换没有意义。我们在下面的实证基准测试中看到的情况并非如此。

ArrayListLinkedList对100000个随机十六进制字符串的列表进行排序所花费的时间大致相同。

小心过早优化

永远不要凭直觉发现性能问题。众所周知,各种技能/经验水平的程序员都不善于发现代码库中的瓶颈。对于高度调优的Java编译器和Java虚拟机实现来说尤其如此。在没有实际证据的情况下担心性能是过早优化的陷阱。

无差异:对100000个元素的ArrayListLinkedList进行排序大约需要0.05秒

做一个快速而肮脏的基准测试表明,实际上您不应该把时间花在ArrayListLinkedList的排序性能上。假设我的基准代码是有效的,那么两个列表的性能几乎相同。我还尝试了SortedSet的两种实现,TreeSetConcurrentSkipListSet,因为您说过您的对象集合是不同的(没有重复的对象)。四个集合中的每一个最多需要十分之一秒来填充和排序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 );

相关内容

  • 没有找到相关文章

最新更新