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就是一个数组
然后看下没有实现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(三)以LinkedList为例,debug看下迭代器的实现.
返回一个迭代器,然后判断是否有下一个节点,有的话就得到当前节点的next,不必再去多于的循环遍历.所以速度肯定很快.
-----------------------------------------------------------------------------------------------------
之前我的疑问在于这个LinkedList的Node节点是如何赋值的,今天又仔细看了下源码才他妈的看到的add的时候给Node复制的!!一定要看全啊....还是数据结构不熟悉