不知道大家在平时的开发中有没有注意到过这个接口,这个接口是一个标志性接口,和Cloneable接口一样,本身没有提供任何接口方法。任何实现RandomAccess接口的对象都可以认为支持快速随机访问的对象。
比如看一下我们最常用的集合类ArrayList和LinkedList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
ArrayList是一个有序集合,顶层实现是基于数组的,LinkedList底层实现是基于双向链表的,来看下如下的测试:
假设我有一个接口,其中的方法入参是一个List,我并不关注这个List具体是什么,我内部就是用for循环去读取它的数据
public class RandomAccessShow {
public void show(List<Integer> showList){
if(showList != null){
int s = showList.size();
for(int k = 0; k < 100;k++){
for(int i = 0;i < s;i++){
showList.get(i);
}
}
}
}
}
现在调用上面这个show方法,并统计下耗时情况,传参类型是分别为ArrayList和LinkedList
public class Test {
public static void main(String[] args){
//初始化集合
List<Integer> list = new ArrayList<Integer>();
//list = new LinkedList<Integer>();
for(int i = 0;i<10000;i++){
list.add(i);
}
long bt = System.currentTimeMillis();
RandomAccessShow test = new RandomAccessShow();
test.show(list);
long et = System.currentTimeMillis();
System.out.println(et-bt);
}
}
运行上面的main方法,ArrayList和LinkedList的耗时分别为11ms和5171ms
修改下show方法,根据传入集合是否实现了RandomAccess接口来选择不同的遍历方式
public class RandomAccessShow {
public void show(List<Integer> showList){
if(showList != null){
if(showList instanceof RandomAccess){
int s = showList.size();
for(int k = 0; k < 100;k++){
for(int i = 0;i < s;i++){
showList.get(i);
}
}
}else {
Iterator<Integer> it = showList.iterator();
for(int k = 0; k < 100;k++){
while (it.hasNext()){
it.next();
}
}
}
}
}
}
运行上面的main方法,ArrayList和LinkedList的耗时分别为11ms和3ms
理解RandomAccess接口可以帮助我们在应用程序中知道正在处理的List是否可以支持快速随机访问,从而能提高程序的性能。
LinkedList的for循环为啥这么耗时呢?这其实跟数据结构有关系,看下LinkedList的get方法如下,其实调用的是node方法:
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
/**
* Returns the (non-null) Node at the specified element index.
*/
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;
}
}
来分析下上面的node方法,首先跟军index的位置判断需要正序遍历还是倒序遍历(这里已经做了一次优化了),然后就是for循环查找,这里就是遍历慢的原因所在
LinkedList的for循环的取值过程如下:
1.get(0),拿到0位的Node<E>中的数据
2.get(1),先拿到0位的Node<E>中的数据,然后再根据0位拿到1位中Node<E>中的数据,
3.get(2),先拿到0位的Node<E>中的数据,然后再根据0位拿到1位中Node<E>中的数据,然后再根据1位拿到2位中Node<E>的数据
4.以此类推
可以看出LinkedList在get任何一个位置的数据时,都会把Node<E>中的index前面的数据过一遍,如果集合中有10条数据,LinkedList的遍历次数是1+2+3+4+5+5+4+3+2+1=30次,而ArrayList只需要遍历10次
LinkedList的Iterator的遍历为什么很快呢?
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
因为next的存在,不需要在遍历前面的节点了




最近浏览
