号外!RandomAccess它是个空接口!

号外!RandomAccess它是个空接口

关于RamdomAccess接口,比较正规的解释为:
用于标明实现该接口的List支持快速随机访问,主要目的是使算法能够在随机和顺序访问的list中表现的更加高效。

那么,接下来,Ted就敲起键盘,带着你撸一撸它,感受一下它的触感。

!它是一个空接口 标记接口

查看源码,发现是这样的

public interface RandomAccess {
}  

什么都没有啊,那它的后代们(实现类)继承它的衣钵(方法)的机会都没有,那它老人家,用来干什么呢? 这种接口称为标记接口,如果某个类implements它,只是标记这个类和它有关系,仅此。

! 标记接口的具体使用

比较经常会用到的就是判断一个list集合,它是否是RandomAccess的一个实现,如果是,do somting 如果不是,do someting else.
此刻我们用到Collections类(内部有很多对集合操作的方法)举例 ,看代码

public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}

在binarySearch()方法中,它要判断传入的list 是否RamdomAccess的实例,如果是,调用indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法
此时,我们就可以看出,RandomAccess接口的作用,就是为了标记,用作实例判断 。
但此时,引申出2个问题

  • 为什么传List 为(List<? extends Comparable<? super T>> list ) 这样奇怪的泛型结构 ?

  • 哪些list 属于randomAccess ,哪些不是呢 ?

于是,我带着问题,遨游在度娘的怀抱中。。。。。

第1个问题,也不难理解,我们一般申明一个list集合时会这样,比如:List <Dog>
第1个?号,它就是指代这个具体的类型(如Dog),而且这个具体类型要extends Comparable,说明这个具体类型一定要实现Comparable接口(java APi规定 重写Comparable接口后才能对自定义对象进行比较,可具体查看本人博文https://blog.csdn.net/ted_cs/article/details/82713706有详细介绍)
Comparable<? super T >这句话,说明Compareable接口,也要传入规定类型,而?我们之前已经清楚 ,是指代List<Dog >中的Dog对象,那么Dog对象的super T ,T应该指的是Dog对象的父类了。

那为什么还要通过super,引出一个父类对象呢,因为父子类间也可以通过CompareTo()方法来比较,比的一般是它们公有的属性的自然顺序。
但因为我自定义的Dog类,我没有自定义父类,但Comparable后面如果不加泛型,又会报错,那么我也可以这样写:

  public class Dog implements Comparable<Dog>{
  重写CompareTo()方法
  }   

至于,第2个问题嘛,其实我们可以翻看java IPA就知道哪些list实现了RandmoAccess接口,
如ArraysList 实现了, 而LinkedList 没有 。

哎,讲到这俩个类,感觉我又要扯出来数据结构才能讲清楚,算了吧,我就简单过一下,然后我们看代码再来感悟一下。

ArraysList实现基础是Array数组,是有序且占用连续的内存;LinkedList实现是Link链表结构,是有序占用非连序内存;ArraysList是数组集合,有索引,可以根据索引快速get到对应数值,而LinkedList,虽然也有索引的概念,但可以理解为隐式的,只是说可以通过索引可以达到正序或倒序的查找结果,有一定优化作用,但本质上,还是一个一个Entry根据双向“指针”的去查找下一个Entry.
估计我讲到这里,有人会蒙圈,没关系,送上救命药给你,请查看 https://blog.csdn.net/ted_cs/article/details/82848350
最终得到的结果是,随着LinkedList的size增加,get方法的有很复杂,远不如AraysList的get的效率。

那回到我们问题,既然实现RandmoAccess接口,可以支持什么快速访问,什么高效算法什么的,那给人家LinkedList也实现好了啊,这们好的东西,怎么可以不分享?
那是因为本身实现快速访问,高效算法什么的,其实是针对数组结构的,因为有索引,让其有了实现的可能 ,而LinkedList不是数组实现的,你丫,就算给你实现这样接口,你丫也没有数组的功能啊,所以数据结构才是问题的根本!!!

那么,理解了这一层,我们再来过过代码,深入感受下

indexedBinarySearch 方法:

private static <T>
int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
int low = 0;
int high = list.size()-1;
 
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = list.get(mid);
int cmp = midVal.compareTo(key);
 
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1);  // key not found
}
  • 请注意,这是一个根据值来查索引的方法
  • 实现二分法查询,list中的对象一定要实现Comparable接口

通过此代码可以看出,其实这就是一个遍历数组的过程,通过索引来获得对应的对象值,因为是数组结构,get方法的效率高

indexedBinarySearch 方法:

private static <T>
int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
{
int low = 0;
int high = list.size()-1;
ListIterator<? extends Comparable<? super T>> i = list.listIterator();
 
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = get(i, mid);
int cmp = midVal.compareTo(key);
 
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1);  // key not found
}  

通过上面的的代码,我们发现link结构的list,调用get方法,传入了ListInterator对象i,和索引mid,
但我们知道,对于Link结构的集合的话,通过iterator遍历是要比普通for循环遍历高效的,所以,判断list没有实现RamdomAccess接口后,其实也相当于知道它不是数组结构,那么就会调用迭代器来进行遍历,提高性能。

讲到这里了,我们应该深刻的认识到

  • linkedList集合,遍历时,由其是huge size时,千万不要用普通for循环,太low效了!!
    End!

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值