java集合学习之List(二)随机访问RandomAccess接口和ArrayList和LinkedList遍历性能问题

ArrayList这个类是实现了RandomAccess接口的,RandomAccess接口和Serializable接口一样都是没有方法或者字段的,像是一个标志,

RandomAccess接口文档说明的是:Marker interface used by List implementations to indicate thatthey support fast (generally constant time) random access. [(标记接口用于List继承表名支持快速随机访问)]

这个接口在Collections类中用的很多,用于判断是否是RandomAccess接口的实例(其实就是多态的应用)[list instanceof RandomAccess]根据是否实现该接口,来选用不同的算

贴源码注释+自己的翻译:

/**
 * Marker interface used by List implementations to indicate that
 * they support fast (generally constant time) random access.  The primary
 * purpose of this interface is to allow generic algorithms to alter their
 * behavior to provide good performance when applied to either random or
 * sequential access lists.
 * 
 * 标记该接口用来被List实现指示他们支持快速随机访问. 这个接口的主要目的就是允许一般算法去修改他们的
 * 行为以提供更好的表现:当被应用与随机或者顺序方式列表时.
 *
 * 

The best algorithms for manipulating random access lists (such as  * ArrayList) can produce quadratic behavior when applied to  * sequential access lists (such as LinkedList).  Generic list  * algorithms are encouraged to check whether the given list is an  * instanceof this interface before applying an algorithm that would  * provide poor performance if it were applied to a sequential access list,  * and to alter their behavior if necessary to guarantee acceptable  * performance.  *  * 操作随机访问列表的lists时比如ArrayList的最好的算法当应用于顺序访问列表如LinkedList时,可能会产生二次项行为  * (妈的,什么是二次项行为啊!韩老师说(a+b)^2展开就是)  * 在应用算法之前通用列表算法鼓励去检查这个list是否是该接口的实例,如果应用于顺序访问列表时,  * 则会提供较差的性能,并且在必要时去警告他们行为来保证可接受的性能.  *  *

It is recognized that the distinction between random and sequential  * access is often fuzzy.  For example, some List implementations  * provide asymptotically linear access times if they get huge, but constant  * access times in practice.  Such a List implementation  * should generally implement this interface.  As a rule of thumb, a  * List implementation should implement this interface if,  * for typical instances of the class, this loop:  *

 *     for (int i=0, n=list.size(); i < n; i++)
 *         list.get(i);
 * 
 * runs faster than this loop:  *
 *     for (Iterator i=list.iterator(); i.hasNext(); )
 *         i.next();
 * 
 *      *  我们意识到在随机访问和顺序访问的区别通常是模糊的,比如,列表很大时,一些list的实现提供了  *  渐进线性访问时间,但在实际中时间是不变的.这样的List应该实现该接口,  *  作为一个经验法则,一个接口应该尽量实现这个这个接口,  *  对于这个接口的实现使用for循环笔使用iterator循环更快  *  *

This interface is a member of the  *  * Java Collections Framework.  *  * @since 1.4  */

 以上是对于RandomAccess接口的描述,一句话概括就是,在使用循环遍历List的时候应该判断下这个集合是否是RandomAccess的实例,如果是就是用for循环来操作,如果不是就是使用iterator迭代器来操作.可是为什么要这么做的?这要从数据结构的角度来说了

首先看一下实现了RandomAccess接口的ArrayList的接口,一般人都知道ArrayList就是一个数组

java集合学习之List(二)随机访问RandomAccess接口和ArrayList和LinkedList遍历性能问题_第1张图片

然后看下没有实现RandomAccess接口的LinkedList,顾名思义就是一个链表

先看一下链表和数组的区别:

数组像是身上都有了编号排成一排的人,你要找第几个直接根据编号去找,就是所谓的根据下标去查询,所以在查询的时候很快,但是如果要插入就变得很慢了,你还要给插入位置之后的其他人重新定义编号,删除同理,效率肯定慢.

链表就像是牵手站成一排的人,你要找第几个就要一个一个取数,从1数到n,但是插入的时候直接把人分开,重新牵手就可以,无需编号,删除同理.

所以如果用for循环遍历的话,贴个代码看看:

/**
 * Collections 测试RandomAccess随机访问速度
*/
List l = new ArrayList();
List _l = new LinkedList();
for(int i=0;i<100000;i++){
    l.add(String.valueOf(i));
    _l.add(String.valueOf(i));
}
long startTime = System.currentTimeMillis();
for(int i=0;i it=_l.iterator();it.hasNext();){
    it.next();
}
System.out.println("count:"+(System.currentTimeMillis()-startTime));

输出结果就是:(ArrayList)count:0 (LinkedList)count:14760 (LinkedList)count:10

惊讶吧!开始分析!ArrayList不用管,就是根据下边直接去查,看下普通for循环下LinkedList为啥贼鸡儿慢!

看一下LinkedList的get方法

判断index在整体的前部还是后部,在前部就从前往后遍历,在后部就从后向前遍历,每次都这样从头遍历,能不慢么,算法复杂度有n^2了吧
Node node(int index) {
        // assert isElementIndex(index);
        if (index < (size >> 1)) {
            Node x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
 
  

 然后看下LinkedList的Iterator方法为啥挺快呢?通过实现类看到:

java集合学习之List(二)随机访问RandomAccess接口和ArrayList和LinkedList遍历性能问题_第2张图片

具体参考java集合学习之List(三)以LinkedList为例,debug看下迭代器的实现.

返回一个迭代器,然后判断是否有下一个节点,有的话就得到当前节点的next,不必再去多于的循环遍历.所以速度肯定很快.

-----------------------------------------------------------------------------------------------------

之前我的疑问在于这个LinkedList的Node节点是如何赋值的,今天又仔细看了下源码才他妈的看到的add的时候给Node复制的!!一定要看全啊....还是数据结构不熟悉

你可能感兴趣的:(java集合)