小白学Java:奇怪的RandomAccess

目录

  • 小白学Java:奇怪的RandomAccess

    • RandomAccess是个啥
    • forLoop与Iterator的区别
    • 判断是否为RandomAccess

小白学Java:奇怪的RandomAccess

我们之前在分析那三个集合源码的时候,曾经说到:ArrayList和Vector继承了RandomAccess接口,但是LinkedList并没有,我们还知道继承了这个接口,就意味着其中元素支持快速随机访问(fast random access)

RandomAccess是个啥

出于好奇,我特意去查看了RandomAccess的官方文档,让我觉得异常惊讶的是!这个接口中啥也没有!是真的奇怪!(事实上和它类似的还有Cloneablejava.io.Serializable,这俩之后会探讨)只留下一串冰冷的英文。

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接口的实现类们是否支持快速随机访问的标志,这样的访问通常只需要常数的时间。
  • 为了啥呢?在访问列表时,根据它们是否是RandomAccess的标记,来选择访问他们的方法以提升性能。

我们知道,ArrayList和Vector底层基于数组实现,内存中占据连续的存储空间,每个元素的下标其实是偏移首地址的偏移量,这样子查询元素只需要根据:元素地址 = 首地址+(元素长度*下标),就可以迅速完成查询,通常只需要花费常数的时间,所以它们理应实现该接口。但是链表不同,链表依据不同节点之间的地址相互引用完成联系,本身不要求地址连续,查询的时候需要遍历的过程,这样子会导致,在数据量比较大的时候,查询元素消耗的时间会很长。

RandomAccess接口的所有实现类:
ArrayList, AttributeList, CopyOnWriteArrayList, RoleList, RoleUnresolvedList, Stack, Vector

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.

可以通过xxList instanceof RandomAccess)判断该列表是否为该接口的实例,如果是顺序访问的列表(如LinkedList),就不应该通过下标索引的方式去查询其中的元素,这样效率会很低。

/*for循环遍历*/
for (int i=0, n=list.size(); i < n; i++)
    list.get(i);
/*Iterator遍历*/
for (Iterator i=list.iterator(); i.hasNext();)
    i.next();

对于实现RandomAccess接口,支持快速随机访问的列表来说,for循环+下标索引遍历的方式比迭代器遍历的方式要更快。

forLoop与Iterator的区别

对此,我们是否可以猜想,如果是LinkedList这样并不支持随即快速访问的列表,是否是Iterator更快呢?于是我们进行一波尝试:

  • 定义关于for循环和Iterator的测试方法
    /*for循环遍历的测试*/
    public static void forTest(List list){
        long start = System.currentTimeMillis();
        for (int i = 0,n=list.size(); i < n; i++) {
            list.get(i);
        }
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println(list.getClass()+" for循环遍历测试 cost:"+time);
    }
    /*Iterator遍历的测试*/
    public static void iteratorTest(List list){
        long start = System.currentTimeMillis();
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            iterator.next();
        }
        long end = System.currentTimeMillis();
        long time = end-start;
        System.out.println(list.getClass()+"迭代器遍历测试 cost:"+time);
    }
  • 测试如下
    public static void main(String[] args) {
        List<Integer> linkedList = new LinkedList<>();
        List<Integer> arrayList = new ArrayList<>();
        /*ArrayList不得不加大数量观察它们的区别,其实差别不大*/
        for (int i = 0; i < 5000000; i++) {
            arrayList.add(i);
        }
        /*LinkedList 这个量级就可以体现比较明显的区别*/
        for(int i = 0;i<50000;i++){
            linkedList.add(i);
        }
        /*方法调用*/
        forTest(arrayList);
        iteratorTest(arrayList);
        forTest(linkedList);
        iteratorTest(linkedList);
    }
  • 测试效果想当的明显

我们可以发现:

  • 对于支持随机访问的列表(如ArrayList),for循环+下标索引的方式和迭代器循环遍历的方式访问数组元素,差别不是很大,在加大数量时,for循环遍历的方式更快一些。
  • 对于不支持随机访问的列表(如LinkedList),两种方式就相当明显了,用for循环+下标索引是相当的慢,因为其每个元素存储的地址并不连续。
  • 综上,如果列表并不支持快速随机访问,访问其元素时,建议使用迭代器;若支持,则可以使用for循环+下标索引。

判断是否为RandomAccess

上面也提到了, 这个空空的接口就是承担着标记的职责(Marker),标记着是否支持随机快速访问,如果不支持的话,还使用索引来遍历的话,效率相当之低。既然有标记,那我们一定有方法去区分标记。这时,我们需要使用instanceof关键字帮助我们做区分,以选择正确的访问方式。

public static void display(List<?> list){
    if(list instanceof RandomAccess){
        //如果支持快速随机访问
        forTest(list);
    }else {
        //不支持快速随机访问,就用迭代器
        iteratorTest(list);
    }
}

再进行一波测试看看,他俩都找到了自己的归宿:

事实上,集合工具类Collections中有许多操作集合的方法,我们随便举一个从前往后填充集合的方法:

    public static <T> void fill(List<? super T> list, T obj) {
        int size = list.size();

        if (size < FILL_THRESHOLD || list instanceof RandomAccess) {
            //for遍历
            for (int i=0; i<size; i++)
                list.set(i, obj);
        } else {
            //迭代器遍历
            ListIterator<? super T> itr = list.listIterator();
            for (int i=0; i<size; i++) {
                itr.next();
                itr.set(obj);
            }
        }
    }

还有许多这样的方法,里面有许多值得学习的地方。我是一个正在学习Java的小白,也许我的知识还未有深度,但是我会努力把自己学习到的做一个体面的总结。对了,如果文章有理解错误,或叙述不清之处,还望大家评论区批评指正。

参考资料:JDK1.8官方文档

原文地址:https://www.cnblogs.com/summerday152/p/12218305.html

时间: 2024-07-30 11:51:08

小白学Java:奇怪的RandomAccess的相关文章

小白学Java:迭代器原来是这么回事

目录 小白学Java:迭代器原来是这么回事 迭代器概述 迭代器设计模式 Iterator定义的方法 迭代器:统一方式 Iterator的总结 小白学Java:迭代器原来是这么回事 前文传送门:Enumeration 上一篇,我们谈到了那个古老的迭代器Enumeration,还谈到了取代他的新迭代器--Iterator.相比于以往,这个新物种又有哪些优点呢? 迭代器这个词,在没查找许多资料之前,我只知道个大概,我知道它可以用来遍历集合,但是至于它其中的奥妙,并没有做深究.本篇文章关于Iterato

小白学Java:I/O流

目录 小白学Java:I/O流 基本分类 发展史 文件字符流 输出的基本结构 流中的异常处理 异常处理新方式 读取的基本结构 运用输入与输出 文件字节流 缓冲流 字符缓冲流 装饰设计模式 转换流(适配器) 适配器设计模式 标准流/系统流 标准流分类 打印流 合并流 序列化/反序列化流 序列化对象 小白学Java:I/O流 流,表示任何有能力产生数据的数据源对象或者是有能力接收数据的接收端对象,它屏蔽了实际的I/O设备中处理数据的细节. 基本分类 根据方向 输入流:数据从外部流向程序,例如从文件中

小白学JAVA if判断 switch 循环

Java if 判断语句 package XunHuanPanDuan;import java.util.Scanner;public class ifDemo01 {    //if 判断语句    public static void main(String[] args) {        Scanner scanner = new Scanner(System.in);        System.out.println("请输入成绩:");        int cj = s

【小白学Java for循环】3分钟学会Java的for循环,让看懂for循环嵌套再不是难事

目录 一.单个for循环介绍 二.for循环嵌套 听讲时能听懂的for循环为什么一做题就晕菜?一个for循环还勉强能看懂,但为什么一看到双重for循环脑子里就感觉脑子全是浆糊? 如果有上述问题那么就继续看这篇文章吧~让你3分钟学会Java的for循环,让看懂for循环嵌套再不是难事! 一.单个for循环介绍 1.语法格式: for(①初始化部分;②循环条件部分;④迭代部分){ ? ③循环体部分 } 例子: 2.执行过程: ①→②→③→④→②→③→④→②→③→④→--→② 3.说明: ②循环条件部

公钥和私钥的区别[小白学java]

一.公钥加密 假设一下,我找了两个数字,一个是1,一个是2.我喜欢2这个数字,就保留起来,不告诉你们(私钥),然后我告诉大家,1是我的公钥.我有一个文件,不能让别人看,我就用1加密了.别人找到了这个文件,但是他不知道2就是解密的私钥啊,所以他解不开,只有我可以用 数字2,就是我的私钥,来解密.这样我就可以保护数据了. 我的好朋友x用我的公钥1加密了字符a,加密后成了b,放在网上.别人偷到了这个文件,但是别人解不开,因为别人不知道2就是我的私钥, 只有我才能解密,解密后就得到a.这样,我们就可以传

零基础java培训靠谱吗?职场转行,零基础开始学Java开发靠谱吗?

学技术转行发展,是职场常见的提升方式,无论是在职充电还是为转行跳槽做准备,选择一个专业技能进行培训学习,都是非常可取的.在能力至上的今天,单凭学历已经不能成为入行敲门砖,特别是在互联网企业,通常在面试过程中就会考核技术能力,此外看你的项目作品,可见技术能力在招聘中是最具说服力的.华清远见教育职业规划专家表示零基础java培训靠谱吗,如果不清楚学什么技术更好,可以访问这里做职业规划,此外还可以通过试学来了解自己对技术课程的兴趣点. 华清远见教育开设的面向零基础人群提供的,从学习到就业一站式的浸入式

【小白的java成长系列】——this关键字

先来看一下下面这段代码: package me.javen.oop; public class ThisDemo { public static void main(String[] args) { Dog dog = new Dog("小花", 10);//创建对象 System.out.println(dog); } } class Dog{ private String name; private int age; public Dog(){}//无参构造 public Dog(

没有基础的初学者学java怎样快速入门?超全的学习路线图

现在地球人都知道互联网行业工资高,上万都是小case,不值一提.可是对于大部分人来说,工资七八千都算很难了.那我也想学java,当程序员,赚大钱.可是作为一个初学者,怎样才可以快速入门呢?早点入门就可以早点赚大钱啊. 划重点:对于没有基础却想快点入门的强烈建议参加培训班. 可以选择自学或者参加培训.自学一般是通过看书.视频入门,了解JAVA这门编程语言的一些基础性知识.但是,毫不夸张地说,很多零基础小白自学JAVA如果直接通过看书,很容易云里雾里,可以说是一个JAVA入门从放弃的....毅种循环

零基础小白入门Java免费视频教程推荐—小猿圈

Java语言作为世界上使用最多的开发语言,企业庞大的需求量让每年进入Java开发领域的人不减反增.对于零基础小白来讲如何学习Java开发语言是入门的第一个难题.本文小猿圈主要给零基础小白入门Java开发技术推荐一个靠谱的Java免费视频教程. 零基础小白入门Java免费视频教程推荐-小猿圈 关于Java基础入门视频教程的选择,网上有各种各样的解说,也有各式各样的视频教程,那么我们究竟应该如何选择Java免费视频教程呢?如何才能挑选到合适自己的Java基础入门视频教程呢? 许多想通过观看Java视