ArrayList这个类是实现了RandomAccess接口的,RandomAccess接口和Serializable接口一样都是没有方法或者字段的,像是一个标志,
RandomAccess接口文档说明的是:Marker interface used by <tt>List</tt> implementations to indicate thatthey support fast (generally constant time) random access. [(标记接口用于List继承表名支持快速随机访问)]
这个接口在Collections类中用的很多,用于判断是否是RandomAccess接口的实例(其实就是多态的应用)[list instanceof RandomAccess]根据是否实现该接口,来选用不同的算
贴源码注释+自己的翻译:
/** * Marker interface used by <tt>List</tt> 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实现指示他们支持快速随机访问. 这个接口的主要目的就是允许一般算法去修改他们的 * 行为以提供更好的表现:当被应用与随机或者顺序方式列表时. * * <p>The best algorithms for manipulating random access lists (such as * <tt>ArrayList</tt>) can produce quadratic behavior when applied to * sequential access lists (such as <tt>LinkedList</tt>). Generic list * algorithms are encouraged to check whether the given list is an * <tt>instanceof</tt> 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是否是该接口的实例,如果应用于顺序访问列表时, * 则会提供较差的性能,并且在必要时去警告他们行为来保证可接受的性能. * * <p>It is recognized that the distinction between random and sequential * access is often fuzzy. For example, some <tt>List</tt> implementations * provide asymptotically linear access times if they get huge, but constant * access times in practice. Such a <tt>List</tt> implementation * should generally implement this interface. As a rule of thumb, a * <tt>List</tt> implementation should implement this interface if, * for typical instances of the class, this loop: * <pre> * for (int i=0, n=list.size(); i < n; i++) * list.get(i); * </pre> * runs faster than this loop: * <pre> * for (Iterator i=list.iterator(); i.hasNext(); ) * i.next(); * </pre> * * 我们意识到在随机访问和顺序访问的区别通常是模糊的,比如,列表很大时,一些list的实现提供了 * 渐进线性访问时间,但在实际中时间是不变的.这样的List应该实现该接口, * 作为一个经验法则,一个接口应该尽量实现这个这个接口, * 对于这个接口的实现使用for循环笔使用iterator循环更快 * * <p>This interface is a member of the * <a href="{@docRoot}/../technotes/guides/collections/index.html"> * Java Collections Framework</a>. * * @since 1.4 */
以上是对于RandomAccess接口的描述,一句话概括就是,在使用循环遍历List的时候应该判断下这个集合是否是RandomAccess的实例,如果是就是用for循环来操作,如果不是就是使用iterator迭代器来操作.可是为什么要这么做的?这要从数据结构的角度来说了
首先看一下实现了RandomAccess接口的ArrayList的接口,一般人都知道ArrayList就是一个数组
然后看下没有实现RandomAccess接口的LinkedList,顾名思义就是一个链表
先看一下链表和数组的区别:
数组像是身上都有了编号排成一排的人,你要找第几个直接根据编号去找,就是所谓的根据下标去查询,所以在查询的时候很快,但是如果要插入就变得很慢了,你还要给插入位置之后的其他人重新定义编号,删除同理,效率肯定慢.
链表就像是牵手站成一排的人,你要找第几个就要一个一个取数,从1数到n,但是插入的时候直接把人分开,重新牵手就可以,无需编号,删除同理.
所以如果用for循环遍历的话,贴个代码看看:
/** * Collections 测试RandomAccess随机访问速度 */ List<String> l = new ArrayList<String>(); List<String> _l = new LinkedList<String>(); 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<l.size();i++){ l.get(i); } System.out.println("count:"+(System.currentTimeMillis()-startTime)); startTime = System.currentTimeMillis(); for(int i=0;i<_l.size();i++){ _l.get(i); } System.out.println("count:"+(System.currentTimeMillis()-startTime)); startTime = System.currentTimeMillis(); for(Iterator<String> 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<E> node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node<E> 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复制的!!一定要看全啊....还是数据结构不熟悉