最近coding的时候对循环的性能很好奇,面对多种循环方式,迭代器、for循环、forEach循环、lambda的forEach循环。

如果单论代码美观,个人偏向于lambda的forEach循环和forEach循环。但是这样唐突的选择很有可能造成性能的下降。

后面本人就分析了一下各个循环之间的效率:

一、ArrayList

size=100000
 for:8
 forEach:12
 Iterator:11
 lambda:115
size=1000for:4
 forEach:1
 Iterator:0
 lambda:98size=10000
for:6
 forEach:2
 Iterator:1
 lambda:107


 

    可以看出,当size很大(10万)时候for循环的性能最高,当size不是很大的时候Iterator和forEach效率就高。ArrayList的底层是数组形式,这不得不从它们的取数逻辑入手,如下:

(1)foreach循环和iterator迭代器:

         foreach的底层是通过iterator循环,通过调用iterator.next(),查看ArrayList对于iterator中next方法的实现可知其最终是通过数组下标获取元素。

(2)for循环

         for语句快里直接通过下标取数

(3)lambda的forEach循环

         开启多线程并发处理

       从如上遍历、取数逻辑中可得出,for在数据量小时,由于多了index++、index<size这两步操作,所以在此时效率要小于foreach循环和iterator迭代器。当数据量大的时候,for对index++、index<size这两步操作的开销小于iterator转化成下标取数的开销了,此时for循环就会变得效率最高了。而lambda的forEach循环由于是开启多线程在取数,频繁线程切换,所以效率也就不尽人意。

 

对于如何选择,总结了如下几点,可参考

1.如果是需要对下标进行处理操作对则选择for循环;

2.对于ArrayList,从代码简洁讲优先考虑foreach,也不用去考虑下标越界对问题;

3.对于实效性要求不高的,如I/O操作,可考虑lambda的forEach循环。

 

二、LinkedList

size=100000
for:18
 forEach:10
 Iterator:9
 lambda:108 
size=10000
for:9
 forEach:3
 Iterator:2
 lambda:104 
size=1000
for:4
 forEach:1
 Iterator:0
 lambda:114

       此时Iterator性能最佳,由于LinkedList的数据结构为链表结构,其中每个节点都记录了前驱和后继结点(头、尾除外)。通过下标的方式获得值都会在链中检查一遍,直到得到我们想要的下标节点为止。而Iterator和forEach则是始终循环子节点,直到尾节点为止。考虑到代码简洁,建议使用forEach。

 

三、HashMap

size=100000

forEach:17

Iterator:13

lambda:125

 

size=10000

forEach:4

Iterator:3

lambda:102

 

size=1000

forEach:1

Iterator:1

lambda:11

 这里我测试了三组数据(都是取entrySet为例),不难发现Iterator循环始终效率方面要占优,这里简单的提下HashMap的放值和取值原理。   

HashMap是一个散列桶,由数组和链表组成。每次put操作都会得到key的hash值,然后根据得到的hash值选择map数组对应的下标存入bucket(存入键对象和值对象)。而get操作时是根据键对象的hash值找map数组中对应的bucket,然后得到对应的值(当存在hash碰撞时,会以链表的形式加在相同hash值的后面,每次get的时候调用keys.equals()得到该键与之对应的值)。

在之前的List对比中已经说明forEach循环底层也是由Iterator实现,而lambda的forEach循环是充分利用cup实现多线程的迭代。所以不难得出HashMap遍历Iterator效率更高一点,但是基于代码简洁性考虑也可采用forEach循环方式。